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

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

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: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 8919d779
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -198,6 +198,31 @@ Description:
		time (in microseconds) this cpu should spend in this idle state
		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/*
Date:		pre-git history
+9 −0
Original line number Diff line number Diff line
@@ -131,6 +131,10 @@ int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
static void enter_s2idle_proper(struct cpuidle_driver *drv,
				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
	 * 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());
	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++;
}

/**
+54 −0
Original line number Diff line number Diff line
@@ -330,6 +330,58 @@ struct cpuidle_state_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(k) (kobj_to_state_obj(k)->state)
#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)
{
	cpuidle_remove_s2idle_attr_group(device->kobjs[i]);
	kobject_put(&device->kobjs[i]->kobj);
	wait_for_completion(&device->kobjs[i]->kobj_unregister);
	kfree(device->kobjs[i]);
@@ -417,6 +470,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device)
			kfree(kobj);
			goto error_state;
		}
		cpuidle_add_s2idle_attr_group(kobj);
		kobject_uevent(&kobj->kobj, KOBJ_ADD);
		device->kobjs[i] = kobj;
	}
+4 −0
Original line number Diff line number Diff line
@@ -33,6 +33,10 @@ struct cpuidle_state_usage {
	unsigned long long	disable;
	unsigned long long	usage;
	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 {