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

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

cpuidle: Add 'above' and 'below' idle state metrics



Add two new metrics for CPU idle states, "above" and "below", to count
the number of times the given state had been asked for (or entered
from the kernel's perspective), but the observed idle duration turned
out to be too short or too long for it (respectively).

These metrics help to estimate the quality of the CPU idle governor
in use.

Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 9456823c
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -145,6 +145,8 @@ What: /sys/devices/system/cpu/cpuX/cpuidle/stateN/name
		/sys/devices/system/cpu/cpuX/cpuidle/stateN/power
		/sys/devices/system/cpu/cpuX/cpuidle/stateN/time
		/sys/devices/system/cpu/cpuX/cpuidle/stateN/usage
		/sys/devices/system/cpu/cpuX/cpuidle/stateN/above
		/sys/devices/system/cpu/cpuX/cpuidle/stateN/below
Date:		September 2007
KernelVersion:	v2.6.24
Contact:	Linux power management list <linux-pm@vger.kernel.org>
@@ -166,6 +168,11 @@ Description:

		usage: (RO) Number of times this state was entered (a count).

		above: (RO) Number of times this state was entered, but the
		       observed CPU idle duration was too short for it (a count).

		below: (RO) Number of times this state was entered, but the
		       observed CPU idle duration was too long for it (a count).

What:		/sys/devices/system/cpu/cpuX/cpuidle/stateN/desc
Date:		February 2008
+10 −0
Original line number Diff line number Diff line
@@ -398,6 +398,16 @@ deeper the (effective) idle state represented by it. Each of them contains
a number of files (attributes) representing the properties of the idle state
object corresponding to it, as follows:

``above``
	Total number of times this idle state had been asked for, but the
	observed idle duration was certainly too short to match its target
	residency.

``below``
	Total number of times this idle state had been asked for, but cerainly
	a deeper idle state would have been a better match for the observed idle
	duration.

``desc``
	Description of the idle state.

+30 −1
Original line number Diff line number Diff line
@@ -202,7 +202,6 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
	struct cpuidle_state *target_state = &drv->states[index];
	bool broadcast = !!(target_state->flags & CPUIDLE_FLAG_TIMER_STOP);
	ktime_t time_start, time_end;
	s64 diff;

	/*
	 * Tell the time framework to switch to a broadcast timer because our
@@ -248,6 +247,9 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
		local_irq_enable();

	if (entered_state >= 0) {
		s64 diff, delay = drv->states[entered_state].exit_latency;
		int i;

		/*
		 * Update cpuidle counters
		 * This can be moved to within driver enter routine,
@@ -260,6 +262,33 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
		dev->last_residency = (int)diff;
		dev->states_usage[entered_state].time += dev->last_residency;
		dev->states_usage[entered_state].usage++;

		if (diff < drv->states[entered_state].target_residency) {
			for (i = entered_state - 1; i >= 0; i--) {
				if (drv->states[i].disabled ||
				    dev->states_usage[i].disable)
					continue;

				/* Shallower states are enabled, so update. */
				dev->states_usage[entered_state].above++;
				break;
			}
		} else if (diff > delay) {
			for (i = entered_state + 1; i < drv->state_count; i++) {
				if (drv->states[i].disabled ||
				    dev->states_usage[i].disable)
					continue;

				/*
				 * Update if a deeper state would have been a
				 * better match for the observed idle duration.
				 */
				if (diff - delay >= drv->states[i].target_residency)
					dev->states_usage[entered_state].below++;

				break;
			}
		}
	} else {
		dev->last_residency = 0;
	}
+6 −0
Original line number Diff line number Diff line
@@ -301,6 +301,8 @@ define_show_state_str_function(name)
define_show_state_str_function(desc)
define_show_state_ull_function(disable)
define_store_state_ull_function(disable)
define_show_state_ull_function(above)
define_show_state_ull_function(below)

define_one_state_ro(name, show_state_name);
define_one_state_ro(desc, show_state_desc);
@@ -310,6 +312,8 @@ define_one_state_ro(power, show_state_power_usage);
define_one_state_ro(usage, show_state_usage);
define_one_state_ro(time, show_state_time);
define_one_state_rw(disable, show_state_disable, store_state_disable);
define_one_state_ro(above, show_state_above);
define_one_state_ro(below, show_state_below);

static struct attribute *cpuidle_state_default_attrs[] = {
	&attr_name.attr,
@@ -320,6 +324,8 @@ static struct attribute *cpuidle_state_default_attrs[] = {
	&attr_usage.attr,
	&attr_time.attr,
	&attr_disable.attr,
	&attr_above.attr,
	&attr_below.attr,
	NULL
};

+2 −0
Original line number Diff line number Diff line
@@ -33,6 +33,8 @@ struct cpuidle_state_usage {
	unsigned long long	disable;
	unsigned long long	usage;
	unsigned long long	time; /* in US */
	unsigned long long	above; /* Number of times it's been too deep */
	unsigned long long	below; /* Number of times it's been too shallow */
#ifdef CONFIG_SUSPEND
	unsigned long long	s2idle_usage;
	unsigned long long	s2idle_time; /* in US */