PM: cpuidle/suspend: Add s2idle usage and time state attributes

Add a new attribute group called "s2idle" under the sysfs directory
of each cpuidle state that supports the ->enter_s2idle callback
and put two new attributes, "usage" and "time", into that group to
represent the number of times the given state was requested for
suspend-to-idle and the total time spent in suspend-to-idle after
requesting that state, respectively.

That will allow diagnostic information related to suspend-to-idle
to be collected without enabling advanced debug features and
analyzing dmesg output.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
Rafael J. Wysocki 2018-03-14 12:27:21 +01:00
parent 8919d779f1
commit 64bdff6980
4 changed files with 92 additions and 0 deletions

View File

@ -198,6 +198,31 @@ Description:
time (in microseconds) this cpu should spend in this idle state time (in microseconds) this cpu should spend in this idle state
to make the transition worth the effort. to make the transition worth the effort.
What: /sys/devices/system/cpu/cpuX/cpuidle/stateN/s2idle/
Date: March 2018
KernelVersion: v4.17
Contact: Linux power management list <linux-pm@vger.kernel.org>
Description:
Idle state usage statistics related to suspend-to-idle.
This attribute group is only present for states that can be
used in suspend-to-idle with suspended timekeeping.
What: /sys/devices/system/cpu/cpuX/cpuidle/stateN/s2idle/time
Date: March 2018
KernelVersion: v4.17
Contact: Linux power management list <linux-pm@vger.kernel.org>
Description:
Total time spent by the CPU in suspend-to-idle (with scheduler
tick suspended) after requesting this state.
What: /sys/devices/system/cpu/cpuX/cpuidle/stateN/s2idle/usage
Date: March 2018
KernelVersion: v4.17
Contact: Linux power management list <linux-pm@vger.kernel.org>
Description:
Total number of times this state has been requested by the CPU
while entering suspend-to-idle.
What: /sys/devices/system/cpu/cpu#/cpufreq/* What: /sys/devices/system/cpu/cpu#/cpufreq/*
Date: pre-git history Date: pre-git history

View File

@ -131,6 +131,10 @@ int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
static void enter_s2idle_proper(struct cpuidle_driver *drv, static void enter_s2idle_proper(struct cpuidle_driver *drv,
struct cpuidle_device *dev, int index) struct cpuidle_device *dev, int index)
{ {
ktime_t time_start, time_end;
time_start = ns_to_ktime(local_clock());
/* /*
* trace_suspend_resume() called by tick_freeze() for the last CPU * trace_suspend_resume() called by tick_freeze() for the last CPU
* executing it contains RCU usage regarded as invalid in the idle * executing it contains RCU usage regarded as invalid in the idle
@ -152,6 +156,11 @@ static void enter_s2idle_proper(struct cpuidle_driver *drv,
*/ */
RCU_NONIDLE(tick_unfreeze()); RCU_NONIDLE(tick_unfreeze());
start_critical_timings(); start_critical_timings();
time_end = ns_to_ktime(local_clock());
dev->states_usage[index].s2idle_time += ktime_us_delta(time_end, time_start);
dev->states_usage[index].s2idle_usage++;
} }
/** /**

View File

@ -330,6 +330,58 @@ struct cpuidle_state_kobj {
struct kobject kobj; struct kobject kobj;
}; };
#ifdef CONFIG_SUSPEND
#define define_show_state_s2idle_ull_function(_name) \
static ssize_t show_state_s2idle_##_name(struct cpuidle_state *state, \
struct cpuidle_state_usage *state_usage, \
char *buf) \
{ \
return sprintf(buf, "%llu\n", state_usage->s2idle_##_name);\
}
define_show_state_s2idle_ull_function(usage);
define_show_state_s2idle_ull_function(time);
#define define_one_state_s2idle_ro(_name, show) \
static struct cpuidle_state_attr attr_s2idle_##_name = \
__ATTR(_name, 0444, show, NULL)
define_one_state_s2idle_ro(usage, show_state_s2idle_usage);
define_one_state_s2idle_ro(time, show_state_s2idle_time);
static struct attribute *cpuidle_state_s2idle_attrs[] = {
&attr_s2idle_usage.attr,
&attr_s2idle_time.attr,
NULL
};
static const struct attribute_group cpuidle_state_s2idle_group = {
.name = "s2idle",
.attrs = cpuidle_state_s2idle_attrs,
};
static void cpuidle_add_s2idle_attr_group(struct cpuidle_state_kobj *kobj)
{
int ret;
if (!kobj->state->enter_s2idle)
return;
ret = sysfs_create_group(&kobj->kobj, &cpuidle_state_s2idle_group);
if (ret)
pr_debug("%s: sysfs attribute group not created\n", __func__);
}
static void cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj *kobj)
{
if (kobj->state->enter_s2idle)
sysfs_remove_group(&kobj->kobj, &cpuidle_state_s2idle_group);
}
#else
static inline void cpuidle_add_s2idle_attr_group(struct cpuidle_state_kobj *kobj) { }
static inline void cpuidle_remove_s2idle_attr_group(struct cpuidle_state_kobj *kobj) { }
#endif /* CONFIG_SUSPEND */
#define kobj_to_state_obj(k) container_of(k, struct cpuidle_state_kobj, kobj) #define kobj_to_state_obj(k) container_of(k, struct cpuidle_state_kobj, kobj)
#define kobj_to_state(k) (kobj_to_state_obj(k)->state) #define kobj_to_state(k) (kobj_to_state_obj(k)->state)
#define kobj_to_state_usage(k) (kobj_to_state_obj(k)->state_usage) #define kobj_to_state_usage(k) (kobj_to_state_obj(k)->state_usage)
@ -383,6 +435,7 @@ static struct kobj_type ktype_state_cpuidle = {
static inline void cpuidle_free_state_kobj(struct cpuidle_device *device, int i) static inline void cpuidle_free_state_kobj(struct cpuidle_device *device, int i)
{ {
cpuidle_remove_s2idle_attr_group(device->kobjs[i]);
kobject_put(&device->kobjs[i]->kobj); kobject_put(&device->kobjs[i]->kobj);
wait_for_completion(&device->kobjs[i]->kobj_unregister); wait_for_completion(&device->kobjs[i]->kobj_unregister);
kfree(device->kobjs[i]); kfree(device->kobjs[i]);
@ -417,6 +470,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device)
kfree(kobj); kfree(kobj);
goto error_state; goto error_state;
} }
cpuidle_add_s2idle_attr_group(kobj);
kobject_uevent(&kobj->kobj, KOBJ_ADD); kobject_uevent(&kobj->kobj, KOBJ_ADD);
device->kobjs[i] = kobj; device->kobjs[i] = kobj;
} }

View File

@ -33,6 +33,10 @@ struct cpuidle_state_usage {
unsigned long long disable; unsigned long long disable;
unsigned long long usage; unsigned long long usage;
unsigned long long time; /* in US */ unsigned long long time; /* in US */
#ifdef CONFIG_SUSPEND
unsigned long long s2idle_usage;
unsigned long long s2idle_time; /* in US */
#endif
}; };
struct cpuidle_state { struct cpuidle_state {