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

PM / arch: x86: MSR_IA32_ENERGY_PERF_BIAS sysfs interface



The Performance and Energy Bias Hint (EPB) is expected to be set by
user space through the generic MSR interface, but that interface is
not particularly nice and there are security concerns regarding it,
so it is not always available.

For this reason, add a sysfs interface for reading and updating the
EPB, in the form of a new attribute, energy_perf_bias, located
under /sys/devices/system/cpu/cpu#/power/ for online CPUs that
support the EPB feature.

Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: default avatarHannes Reinecke <hare@suse.com>
Acked-by: default avatarBorislav Petkov <bp@suse.de>
parent 5861381d
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -518,3 +518,21 @@ Description: Control Symetric Multi Threading (SMT)

			 If control status is "forceoff" or "notsupported" writes
			 are rejected.

What:		/sys/devices/system/cpu/cpu#/power/energy_perf_bias
Date:		March 2019
Contact:	linux-pm@vger.kernel.org
Description:	Intel Energy and Performance Bias Hint (EPB)

		EPB for the given CPU in a sliding scale 0 - 15, where a value
		of 0 corresponds to a hint preference for highest performance
		and a value of 15 corresponds to the maximum energy savings.

		In order to change the EPB value for the CPU, write either
		a number in the 0 - 15 sliding scale above, or one of the
		strings: "performance", "balance-performance", "normal",
		"balance-power", "power" (that represent values reflected by
		their meaning), to this attribute.

		This attribute is present for all online CPUs supporting the
		Intel EPB feature.
+27 −0
Original line number Diff line number Diff line
@@ -4,3 +4,30 @@ Intel Performance and Energy Bias Hint

.. kernel-doc:: arch/x86/kernel/cpu/intel_epb.c
   :doc: overview

Intel Performance and Energy Bias Attribute in ``sysfs``
========================================================

The Intel Performance and Energy Bias Hint (EPB) value for a given (logical) CPU
can be checked or updated through a ``sysfs`` attribute (file) under
:file:`/sys/devices/system/cpu/cpu<N>/power/`, where the CPU number ``<N>``
is allocated at the system initialization time:

``energy_perf_bias``
	Shows the current EPB value for the CPU in a sliding scale 0 - 15, where
	a value of 0 corresponds to a hint preference for highest performance
	and a value of 15 corresponds to the maximum energy savings.

	In order to update the EPB value for the CPU, this attribute can be
	written to, either with a number in the 0 - 15 sliding scale above, or
	with one of the strings: "performance", "balance-performance", "normal",
	"balance-power", "power" that represent values reflected by their
	meaning.

	This attribute is present for all online CPUs supporting the EPB
	feature.

Note that while the EPB interface to the processor is defined at the logical CPU
level, the physical register backing it may be shared by multiple CPUs (for
example, SMT siblings or cores in one package).  For this reason, updating the
EPB value for one CPU may cause the EPB values for other CPUs to change.
+89 −4
Original line number Diff line number Diff line
@@ -9,8 +9,12 @@
 */

#include <linux/cpuhotplug.h>
#include <linux/cpu.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/syscore_ops.h>
#include <linux/pm.h>

#include <asm/cpufeature.h>
#include <asm/msr.h>
@@ -20,9 +24,9 @@
 *
 * The Performance and Energy Bias Hint (EPB) allows software to specify its
 * preference with respect to the power-performance tradeoffs present in the
 * processor.  Generally, the EPB is expected to be set by user space through
 * the generic MSR interface (with the help of the x86_energy_perf_policy tool),
 * but there are two reasons for the kernel to touch it.
 * processor.  Generally, the EPB is expected to be set by user space (directly
 * via sysfs or with the help of the x86_energy_perf_policy tool), but there are
 * two reasons for the kernel to update it.
 *
 * First, there are systems where the platform firmware resets the EPB during
 * system-wide transitions from sleep states back into the working state
@@ -52,6 +56,7 @@ static DEFINE_PER_CPU(u8, saved_epb);

#define EPB_MASK	0x0fULL
#define EPB_SAVED	0x10ULL
#define MAX_EPB		EPB_MASK

static int intel_epb_save(void)
{
@@ -97,15 +102,95 @@ static struct syscore_ops intel_epb_syscore_ops = {
	.resume = intel_epb_restore,
};

static const char * const energy_perf_strings[] = {
	"performance",
	"balance-performance",
	"normal",
	"balance-power",
	"power"
};
static const u8 energ_perf_values[] = {
	ENERGY_PERF_BIAS_PERFORMANCE,
	ENERGY_PERF_BIAS_BALANCE_PERFORMANCE,
	ENERGY_PERF_BIAS_NORMAL,
	ENERGY_PERF_BIAS_BALANCE_POWERSAVE,
	ENERGY_PERF_BIAS_POWERSAVE
};

static ssize_t energy_perf_bias_show(struct device *dev,
				     struct device_attribute *attr,
				     char *buf)
{
	unsigned int cpu = dev->id;
	u64 epb;
	int ret;

	ret = rdmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS, &epb);
	if (ret < 0)
		return ret;

	return sprintf(buf, "%llu\n", epb);
}

static ssize_t energy_perf_bias_store(struct device *dev,
				      struct device_attribute *attr,
				      const char *buf, size_t count)
{
	unsigned int cpu = dev->id;
	u64 epb, val;
	int ret;

	ret = __sysfs_match_string(energy_perf_strings,
				   ARRAY_SIZE(energy_perf_strings), buf);
	if (ret >= 0)
		val = energ_perf_values[ret];
	else if (kstrtou64(buf, 0, &val) || val > MAX_EPB)
		return -EINVAL;

	ret = rdmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS, &epb);
	if (ret < 0)
		return ret;

	ret = wrmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS,
			    (epb & ~EPB_MASK) | val);
	if (ret < 0)
		return ret;

	return count;
}

static DEVICE_ATTR_RW(energy_perf_bias);

static struct attribute *intel_epb_attrs[] = {
	&dev_attr_energy_perf_bias.attr,
	NULL
};

static const struct attribute_group intel_epb_attr_group = {
	.name = power_group_name,
	.attrs =  intel_epb_attrs
};

static int intel_epb_online(unsigned int cpu)
{
	struct device *cpu_dev = get_cpu_device(cpu);

	intel_epb_restore();
	if (!cpuhp_tasks_frozen)
		sysfs_merge_group(&cpu_dev->kobj, &intel_epb_attr_group);

	return 0;
}

static int intel_epb_offline(unsigned int cpu)
{
	return intel_epb_save();
	struct device *cpu_dev = get_cpu_device(cpu);

	if (!cpuhp_tasks_frozen)
		sysfs_unmerge_group(&cpu_dev->kobj, &intel_epb_attr_group);

	intel_epb_save();
	return 0;
}

static __init int intel_epb_init(void)