History

PM / wakeup: Show wakeup sources stats in sysfs

Add an ID and a device pointer to 'struct wakeup_source'. Use them to to expose wakeup sources statistics in sysfs under /sys/class/wakeup/wakeup<ID>/*.

Tri Vo committed 7 years ago

Commit c8377ad1 parent 2434aea11 files · +328 −13Browse files

Unified · Split

11 files
  1. Documentation/ABI/testing/sysfs-class-wakeup +76 −0
  2. drivers/acpi/device_pm.c +2 −1
  3. drivers/base/power/Makefile +1 −1
  4. drivers/base/power/power.h +9 −0
  5. drivers/base/power/wakeup.c +24 −4
  6. drivers/base/power/wakeup_stats.c +203 −0
  7. fs/eventpoll.c +2 −2
  8. include/linux/pm_wakeup.h +8 −2
  9. kernel/power/autosleep.c +1 −1
  10. kernel/power/wakelock.c +1 −1
  11. kernel/time/alarmtimer.c +1 −1
Documentation/ABI/testing/sysfs-class-wakeup+76−0
@@ -0,0 +1,76 @@
1What: /sys/class/wakeup/
2Date: June 2019
3Contact: Tri Vo <trong@android.com>
4Description:
5 The /sys/class/wakeup/ directory contains pointers to all
6 wakeup sources in the kernel at that moment in time.
7
8What: /sys/class/wakeup/.../name
9Date: June 2019
10Contact: Tri Vo <trong@android.com>
11Description:
12 This file contains the name of the wakeup source.
13
14What: /sys/class/wakeup/.../active_count
15Date: June 2019
16Contact: Tri Vo <trong@android.com>
17Description:
18 This file contains the number of times the wakeup source was
19 activated.
20
21What: /sys/class/wakeup/.../event_count
22Date: June 2019
23Contact: Tri Vo <trong@android.com>
24Description:
25 This file contains the number of signaled wakeup events
26 associated with the wakeup source.
27
28What: /sys/class/wakeup/.../wakeup_count
29Date: June 2019
30Contact: Tri Vo <trong@android.com>
31Description:
32 This file contains the number of times the wakeup source might
33 abort suspend.
34
35What: /sys/class/wakeup/.../expire_count
36Date: June 2019
37Contact: Tri Vo <trong@android.com>
38Description:
39 This file contains the number of times the wakeup source's
40 timeout has expired.
41
42What: /sys/class/wakeup/.../active_time_ms
43Date: June 2019
44Contact: Tri Vo <trong@android.com>
45Description:
46 This file contains the amount of time the wakeup source has
47 been continuously active, in milliseconds. If the wakeup
48 source is not active, this file contains '0'.
49
50What: /sys/class/wakeup/.../total_time_ms
51Date: June 2019
52Contact: Tri Vo <trong@android.com>
53Description:
54 This file contains the total amount of time this wakeup source
55 has been active, in milliseconds.
56
57What: /sys/class/wakeup/.../max_time_ms
58Date: June 2019
59Contact: Tri Vo <trong@android.com>
60Description:
61 This file contains the maximum amount of time this wakeup
62 source has been continuously active, in milliseconds.
63
64What: /sys/class/wakeup/.../last_change_ms
65Date: June 2019
66Contact: Tri Vo <trong@android.com>
67Description:
68 This file contains the monotonic clock time when the wakeup
69 source was touched last time, in milliseconds.
70
71What: /sys/class/wakeup/.../prevent_suspend_time_ms
72Date: June 2019
73Contact: Tri Vo <trong@android.com>
74Description:
75 The file contains the total amount of time this wakeup source
76 has been preventing autosleep, in milliseconds.
drivers/acpi/device_pm.c+2−1
@@ -497,7 +497,8 @@ acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev, · 496 unmodified lines
497 goto out;497 goto out;
498 498
499 mutex_lock(&acpi_pm_notifier_lock);499 mutex_lock(&acpi_pm_notifier_lock);
500 adev->wakeup.ws = wakeup_source_register(dev_name(&adev->dev));500 adev->wakeup.ws = wakeup_source_register(&adev->dev,
501 dev_name(&adev->dev));
501 adev->wakeup.context.dev = dev;502 adev->wakeup.context.dev = dev;
502 adev->wakeup.context.func = func;503 adev->wakeup.context.func = func;
503 adev->wakeup.flags.notifier_present = true;504 adev->wakeup.flags.notifier_present = true;
drivers/base/power/Makefile+1−1
@@ -1,6 +1,6 @@
1# SPDX-License-Identifier: GPL-2.01# SPDX-License-Identifier: GPL-2.0
2obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o2obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o
3obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o3obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o wakeup_stats.o
4obj-$(CONFIG_PM_TRACE_RTC) += trace.o4obj-$(CONFIG_PM_TRACE_RTC) += trace.o
5obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o5obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o
6obj-$(CONFIG_HAVE_CLK) += clock_ops.o6obj-$(CONFIG_HAVE_CLK) += clock_ops.o
drivers/base/power/power.h+9−0
@@ -149,3 +149,12 @@ static inline void device_pm_init(struct device *dev) · 148 unmodified lines
149 device_pm_sleep_init(dev);149 device_pm_sleep_init(dev);
150 pm_runtime_init(dev);150 pm_runtime_init(dev);
151}151}
152
153#ifdef CONFIG_PM_SLEEP
154
155/* drivers/base/power/wakeup_stats.c */
156extern int wakeup_source_sysfs_add(struct device *parent,
157 struct wakeup_source *ws);
158extern void wakeup_source_sysfs_remove(struct wakeup_source *ws);
159
160#endif /* CONFIG_PM_SLEEP */
drivers/base/power/wakeup.c+24−4
@@ -72,6 +72,8 @@ static struct wakeup_source deleted_ws = { · 71 unmodified lines
72 .lock = __SPIN_LOCK_UNLOCKED(deleted_ws.lock),72 .lock = __SPIN_LOCK_UNLOCKED(deleted_ws.lock),
73};73};
74 74
75static DEFINE_IDA(wakeup_ida);
76
75/**77/**
76 * wakeup_source_create - Create a struct wakeup_source object.78 * wakeup_source_create - Create a struct wakeup_source object.
77 * @name: Name of the new wakeup source.79 * @name: Name of the new wakeup source.
@@ -80,6 +82,7 @@ struct wakeup_source *wakeup_source_create(const char *name) · 2 unmodified lines
80{82{
81 struct wakeup_source *ws;83 struct wakeup_source *ws;
82 const char *ws_name;84 const char *ws_name;
85 int id;
83 86
84 ws = kzalloc(sizeof(*ws), GFP_KERNEL);87 ws = kzalloc(sizeof(*ws), GFP_KERNEL);
85 if (!ws)88 if (!ws)
@@ -90,8 +93,15 @@ struct wakeup_source *wakeup_source_create(const char *name) · 4 unmodified lines
90 goto err_name;93 goto err_name;
91 ws->name = ws_name;94 ws->name = ws_name;
92 95
96 id = ida_alloc(&wakeup_ida, GFP_KERNEL);
97 if (id < 0)
98 goto err_id;
99 ws->id = id;
100
93 return ws;101 return ws;
94 102
103err_id:
104 kfree_const(ws->name);
95err_name:105err_name:
96 kfree(ws);106 kfree(ws);
97err_ws:107err_ws:
@@ -140,6 +150,7 @@ void wakeup_source_destroy(struct wakeup_source *ws) · 42 unmodified lines
140 150
141 __pm_relax(ws);151 __pm_relax(ws);
142 wakeup_source_record(ws);152 wakeup_source_record(ws);
153 ida_free(&wakeup_ida, ws->id);
143 kfree_const(ws->name);154 kfree_const(ws->name);
144 kfree(ws);155 kfree(ws);
145}156}
@@ -193,16 +204,24 @@ EXPORT_SYMBOL_GPL(wakeup_source_remove); · 47 unmodified lines
193 204
194/**205/**
195 * wakeup_source_register - Create wakeup source and add it to the list.206 * wakeup_source_register - Create wakeup source and add it to the list.
207 * @dev: Device this wakeup source is associated with (or NULL if virtual).
196 * @name: Name of the wakeup source to register.208 * @name: Name of the wakeup source to register.
197 */209 */
198struct wakeup_source *wakeup_source_register(const char *name)210struct wakeup_source *wakeup_source_register(struct device *dev,
211 const char *name)
199{212{
200 struct wakeup_source *ws;213 struct wakeup_source *ws;
214 int ret;
201 215
202 ws = wakeup_source_create(name);216 ws = wakeup_source_create(name);
203 if (ws)217 if (ws) {
218 ret = wakeup_source_sysfs_add(dev, ws);
219 if (ret) {
220 wakeup_source_destroy(ws);
221 return NULL;
222 }
204 wakeup_source_add(ws);223 wakeup_source_add(ws);
205 224 }
206 return ws;225 return ws;
207}226}
208EXPORT_SYMBOL_GPL(wakeup_source_register);227EXPORT_SYMBOL_GPL(wakeup_source_register);
@@ -215,6 +234,7 @@ void wakeup_source_unregister(struct wakeup_source *ws) · 6 unmodified lines
215{234{
216 if (ws) {235 if (ws) {
217 wakeup_source_remove(ws);236 wakeup_source_remove(ws);
237 wakeup_source_sysfs_remove(ws);
218 wakeup_source_destroy(ws);238 wakeup_source_destroy(ws);
219 }239 }
220}240}
@@ -258,7 +278,7 @@ int device_wakeup_enable(struct device *dev) · 37 unmodified lines
258 if (pm_suspend_target_state != PM_SUSPEND_ON)278 if (pm_suspend_target_state != PM_SUSPEND_ON)
259 dev_dbg(dev, "Suspicious %s() during system transition!\n", __func__);279 dev_dbg(dev, "Suspicious %s() during system transition!\n", __func__);
260 280
261 ws = wakeup_source_register(dev_name(dev));281 ws = wakeup_source_register(dev, dev_name(dev));
262 if (!ws)282 if (!ws)
263 return -ENOMEM;283 return -ENOMEM;
264 284
drivers/base/power/wakeup_stats.c+203−0
@@ -0,0 +1,203 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Wakeup statistics in sysfs
4 *
5 * Copyright (c) 2019 Linux Foundation
6 * Copyright (c) 2019 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
7 * Copyright (c) 2019 Google Inc.
8 */
9
10#include <linux/device.h>
11#include <linux/idr.h>
12#include <linux/init.h>
13#include <linux/kdev_t.h>
14#include <linux/kernel.h>
15#include <linux/kobject.h>
16#include <linux/slab.h>
17#include <linux/timekeeping.h>
18
19#include "power.h"
20
21static struct class *wakeup_class;
22
23#define wakeup_attr(_name) \
24static ssize_t _name##_show(struct device *dev, \
25 struct device_attribute *attr, char *buf) \
26{ \
27 struct wakeup_source *ws = dev_get_drvdata(dev); \
28 \
29 return sprintf(buf, "%lu\n", ws->_name); \
30} \
31static DEVICE_ATTR_RO(_name)
32
33wakeup_attr(active_count);
34wakeup_attr(event_count);
35wakeup_attr(wakeup_count);
36wakeup_attr(expire_count);
37
38static ssize_t active_time_ms_show(struct device *dev,
39 struct device_attribute *attr, char *buf)
40{
41 struct wakeup_source *ws = dev_get_drvdata(dev);
42 ktime_t active_time =
43 ws->active ? ktime_sub(ktime_get(), ws->last_time) : 0;
44
45 return sprintf(buf, "%lld\n", ktime_to_ms(active_time));
46}
47static DEVICE_ATTR_RO(active_time_ms);
48
49static ssize_t total_time_ms_show(struct device *dev,
50 struct device_attribute *attr, char *buf)
51{
52 struct wakeup_source *ws = dev_get_drvdata(dev);
53 ktime_t active_time;
54 ktime_t total_time = ws->total_time;
55
56 if (ws->active) {
57 active_time = ktime_sub(ktime_get(), ws->last_time);
58 total_time = ktime_add(total_time, active_time);
59 }
60 return sprintf(buf, "%lld\n", ktime_to_ms(total_time));
61}
62static DEVICE_ATTR_RO(total_time_ms);
63
64static ssize_t max_time_ms_show(struct device *dev,
65 struct device_attribute *attr, char *buf)
66{
67 struct wakeup_source *ws = dev_get_drvdata(dev);
68 ktime_t active_time;
69 ktime_t max_time = ws->max_time;
70
71 if (ws->active) {
72 active_time = ktime_sub(ktime_get(), ws->last_time);
73 if (active_time > max_time)
74 max_time = active_time;
75 }
76 return sprintf(buf, "%lld\n", ktime_to_ms(max_time));
77}
78static DEVICE_ATTR_RO(max_time_ms);
79
80static ssize_t last_change_ms_show(struct device *dev,
81 struct device_attribute *attr, char *buf)
82{
83 struct wakeup_source *ws = dev_get_drvdata(dev);
84
85 return sprintf(buf, "%lld\n", ktime_to_ms(ws->last_time));
86}
87static DEVICE_ATTR_RO(last_change_ms);
88
89static ssize_t name_show(struct device *dev, struct device_attribute *attr,
90 char *buf)
91{
92 struct wakeup_source *ws = dev_get_drvdata(dev);
93
94 return sprintf(buf, "%s\n", ws->name);
95}
96static DEVICE_ATTR_RO(name);
97
98static ssize_t prevent_suspend_time_ms_show(struct device *dev,
99 struct device_attribute *attr,
100 char *buf)
101{
102 struct wakeup_source *ws = dev_get_drvdata(dev);
103 ktime_t prevent_sleep_time = ws->prevent_sleep_time;
104
105 if (ws->active && ws->autosleep_enabled) {
106 prevent_sleep_time = ktime_add(prevent_sleep_time,
107 ktime_sub(ktime_get(), ws->start_prevent_time));
108 }
109 return sprintf(buf, "%lld\n", ktime_to_ms(prevent_sleep_time));
110}
111static DEVICE_ATTR_RO(prevent_suspend_time_ms);
112
113static struct attribute *wakeup_source_attrs[] = {
114 &dev_attr_name.attr,
115 &dev_attr_active_count.attr,
116 &dev_attr_event_count.attr,
117 &dev_attr_wakeup_count.attr,
118 &dev_attr_expire_count.attr,
119 &dev_attr_active_time_ms.attr,
120 &dev_attr_total_time_ms.attr,
121 &dev_attr_max_time_ms.attr,
122 &dev_attr_last_change_ms.attr,
123 &dev_attr_prevent_suspend_time_ms.attr,
124 NULL,
125};
126ATTRIBUTE_GROUPS(wakeup_source);
127
128static void device_create_release(struct device *dev)
129{
130 kfree(dev);
131}
132
133static struct device *wakeup_source_device_create(struct device *parent,
134 struct wakeup_source *ws)
135{
136 struct device *dev = NULL;
137 int retval = -ENODEV;
138
139 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
140 if (!dev) {
141 retval = -ENOMEM;
142 goto error;
143 }
144
145 device_initialize(dev);
146 dev->devt = MKDEV(0, 0);
147 dev->class = wakeup_class;
148 dev->parent = parent;
149 dev->groups = wakeup_source_groups;
150 dev->release = device_create_release;
151 dev_set_drvdata(dev, ws);
152 device_set_pm_not_required(dev);
153
154 retval = kobject_set_name(&dev->kobj, "wakeup%d", ws->id);
155 if (retval)
156 goto error;
157
158 retval = device_add(dev);
159 if (retval)
160 goto error;
161
162 return dev;
163
164error:
165 put_device(dev);
166 return ERR_PTR(retval);
167}
168
169/**
170 * wakeup_source_sysfs_add - Add wakeup_source attributes to sysfs.
171 * @parent: Device given wakeup source is associated with (or NULL if virtual).
172 * @ws: Wakeup source to be added in sysfs.
173 */
174int wakeup_source_sysfs_add(struct device *parent, struct wakeup_source *ws)
175{
176 struct device *dev;
177
178 dev = wakeup_source_device_create(parent, ws);
179 if (IS_ERR(dev))
180 return PTR_ERR(dev);
181 ws->dev = dev;
182
183 return 0;
184}
185EXPORT_SYMBOL_GPL(wakeup_source_sysfs_add);
186
187/**
188 * wakeup_source_sysfs_remove - Remove wakeup_source attributes from sysfs.
189 * @ws: Wakeup source to be removed from sysfs.
190 */
191void wakeup_source_sysfs_remove(struct wakeup_source *ws)
192{
193 device_unregister(ws->dev);
194}
195EXPORT_SYMBOL_GPL(wakeup_source_sysfs_remove);
196
197static int __init wakeup_sources_sysfs_init(void)
198{
199 wakeup_class = class_create(THIS_MODULE, "wakeup");
200
201 return PTR_ERR_OR_ZERO(wakeup_class);
202}
203postcore_initcall(wakeup_sources_sysfs_init);
fs/eventpoll.c+2−2
@@ -1459,13 +1459,13 @@ static int ep_create_wakeup_source(struct epitem *epi) · 1458 unmodified lines
1459 struct wakeup_source *ws;1459 struct wakeup_source *ws;
1460 1460
1461 if (!epi->ep->ws) {1461 if (!epi->ep->ws) {
1462 epi->ep->ws = wakeup_source_register("eventpoll");1462 epi->ep->ws = wakeup_source_register(NULL, "eventpoll");
1463 if (!epi->ep->ws)1463 if (!epi->ep->ws)
1464 return -ENOMEM;1464 return -ENOMEM;
1465 }1465 }
1466 1466
1467 name = epi->ffd.file->f_path.dentry->d_name.name;1467 name = epi->ffd.file->f_path.dentry->d_name.name;
1468 ws = wakeup_source_register(name);1468 ws = wakeup_source_register(NULL, name);
1469 1469
1470 if (!ws)1470 if (!ws)
1471 return -ENOMEM;1471 return -ENOMEM;
include/linux/pm_wakeup.h+8−2
@@ -21,6 +21,7 @@ struct wake_irq; · 20 unmodified lines
21 * struct wakeup_source - Representation of wakeup sources21 * struct wakeup_source - Representation of wakeup sources
22 *22 *
23 * @name: Name of the wakeup source23 * @name: Name of the wakeup source
24 * @id: Wakeup source id
24 * @entry: Wakeup source list entry25 * @entry: Wakeup source list entry
25 * @lock: Wakeup source lock26 * @lock: Wakeup source lock
26 * @wakeirq: Optional device specific wakeirq27 * @wakeirq: Optional device specific wakeirq
@@ -35,11 +36,13 @@ struct wake_irq; · 8 unmodified lines
35 * @relax_count: Number of times the wakeup source was deactivated.36 * @relax_count: Number of times the wakeup source was deactivated.
36 * @expire_count: Number of times the wakeup source's timeout has expired.37 * @expire_count: Number of times the wakeup source's timeout has expired.
37 * @wakeup_count: Number of times the wakeup source might abort suspend.38 * @wakeup_count: Number of times the wakeup source might abort suspend.
39 * @dev: Struct device for sysfs statistics about the wakeup source.
38 * @active: Status of the wakeup source.40 * @active: Status of the wakeup source.
39 * @autosleep_enabled: Autosleep is active, so update @prevent_sleep_time.41 * @autosleep_enabled: Autosleep is active, so update @prevent_sleep_time.
40 */42 */
41struct wakeup_source {43struct wakeup_source {
42 const char *name;44 const char *name;
45 int id;
43 struct list_head entry;46 struct list_head entry;
44 spinlock_t lock;47 spinlock_t lock;
45 struct wake_irq *wakeirq;48 struct wake_irq *wakeirq;
@@ -55,6 +58,7 @@ struct wakeup_source { · 9 unmodified lines
55 unsigned long relax_count;58 unsigned long relax_count;
56 unsigned long expire_count;59 unsigned long expire_count;
57 unsigned long wakeup_count;60 unsigned long wakeup_count;
61 struct device *dev;
58 bool active:1;62 bool active:1;
59 bool autosleep_enabled:1;63 bool autosleep_enabled:1;
60};64};
@@ -85,7 +89,8 @@ extern struct wakeup_source *wakeup_source_create(const char *name); · 24 unmodified lines
85extern void wakeup_source_destroy(struct wakeup_source *ws);89extern void wakeup_source_destroy(struct wakeup_source *ws);
86extern void wakeup_source_add(struct wakeup_source *ws);90extern void wakeup_source_add(struct wakeup_source *ws);
87extern void wakeup_source_remove(struct wakeup_source *ws);91extern void wakeup_source_remove(struct wakeup_source *ws);
88extern struct wakeup_source *wakeup_source_register(const char *name);92extern struct wakeup_source *wakeup_source_register(struct device *dev,
93 const char *name);
89extern void wakeup_source_unregister(struct wakeup_source *ws);94extern void wakeup_source_unregister(struct wakeup_source *ws);
90extern int device_wakeup_enable(struct device *dev);95extern int device_wakeup_enable(struct device *dev);
91extern int device_wakeup_disable(struct device *dev);96extern int device_wakeup_disable(struct device *dev);
@@ -122,7 +127,8 @@ static inline void wakeup_source_add(struct wakeup_source *ws) {} · 30 unmodified lines
122 127
123static inline void wakeup_source_remove(struct wakeup_source *ws) {}128static inline void wakeup_source_remove(struct wakeup_source *ws) {}
124 129
125static inline struct wakeup_source *wakeup_source_register(const char *name)130static inline struct wakeup_source *wakeup_source_register(struct device *dev,
131 const char *name)
126{132{
127 return NULL;133 return NULL;
128}134}
kernel/power/autosleep.c+1−1
@@ -116,7 +116,7 @@ int pm_autosleep_set_state(suspend_state_t state) · 115 unmodified lines
116 116
117int __init pm_autosleep_init(void)117int __init pm_autosleep_init(void)
118{118{
119 autosleep_ws = wakeup_source_register("autosleep");119 autosleep_ws = wakeup_source_register(NULL, "autosleep");
120 if (!autosleep_ws)120 if (!autosleep_ws)
121 return -ENOMEM;121 return -ENOMEM;
122 122
kernel/power/wakelock.c+1−1
@@ -188,7 +188,7 @@ static struct wakelock *wakelock_lookup_add(const char *name, size_t len, · 187 unmodified lines
188 return ERR_PTR(-ENOMEM);188 return ERR_PTR(-ENOMEM);
189 }189 }
190 190
191 wl->ws = wakeup_source_register(wl->name);191 wl->ws = wakeup_source_register(NULL, wl->name);
192 if (!wl->ws) {192 if (!wl->ws) {
193 kfree(wl->name);193 kfree(wl->name);
194 kfree(wl);194 kfree(wl);
kernel/time/alarmtimer.c+1−1
@@ -97,7 +97,7 @@ static int alarmtimer_rtc_add_device(struct device *dev, · 96 unmodified lines
97 if (!device_may_wakeup(rtc->dev.parent))97 if (!device_may_wakeup(rtc->dev.parent))
98 return -1;98 return -1;
99 99
100 __ws = wakeup_source_register("alarmtimer");100 __ws = wakeup_source_register(dev, "alarmtimer");
101 101
102 spin_lock_irqsave(&rtcdev_lock, flags);102 spin_lock_irqsave(&rtcdev_lock, flags);
103 if (!rtcdev) {103 if (!rtcdev) {

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t