Commit 440f9d47 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branches 'pm-cpuidle', 'pm-sleep' and 'pm-powercap'

Merge cpuidle updates, changes related to system sleep and power capping
updates for 6.10:

 - Fix kerneldoc description of ladder_do_selection() (Jeff Johnson).

 - Convert the cpuidle kirkwood driver to platform remove callback
   returning void (Yangtao Li).

 - Replace deprecated strncpy() with strscpy() in the hibernation core
   code (Justin Stitt).

 - Use %ps to simplify debug output in the core system-wide suspend and
   resume code (Len Brown).

 - Remove unnecessary else from device_init_wakeup() and make
   device_wakeup_disable() return void (Dhruva Gole).

 - Enable PMU support in the Intel TPMI RAPL driver (Zhang Rui).

 - Add support for ArrowLake-H platform to the Intel RAPL driver (Zhang
   Rui).

 - Avoid explicit cpumask allocation on stack in DTPM (Dawei Li).

* pm-cpuidle:
  cpuidle: ladder: fix ladder_do_selection() kernel-doc
  cpuidle: kirkwood: Convert to platform remove callback returning void

* pm-sleep:
  PM: hibernate: replace deprecated strncpy() with strscpy()
  PM: sleep: Take advantage of %ps to simplify debug output
  PM: wakeup: Remove unnecessary else from device_init_wakeup()
  PM: wakeup: make device_wakeup_disable() return void

* pm-powercap:
  powercap: intel_rapl_tpmi: Enable PMU support
  powercap: intel_rapl: Introduce APIs for PMU support
  powercap: intel_rapl: Sort header files
  powercap: intel_rapl: Add support for ArrowLake-H platform
  powercap: DTPM: Avoid explicit cpumask allocation on stack
......@@ -208,7 +208,7 @@ static ktime_t initcall_debug_start(struct device *dev, void *cb)
if (!pm_print_times_enabled)
return 0;
dev_info(dev, "calling %pS @ %i, parent: %s\n", cb,
dev_info(dev, "calling %ps @ %i, parent: %s\n", cb,
task_pid_nr(current),
dev->parent ? dev_name(dev->parent) : "none");
return ktime_get();
......@@ -223,7 +223,7 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime,
return;
rettime = ktime_get();
dev_info(dev, "%pS returned %d after %Ld usecs\n", cb, error,
dev_info(dev, "%ps returned %d after %Ld usecs\n", cb, error,
(unsigned long long)ktime_us_delta(rettime, calltime));
}
......@@ -1927,7 +1927,7 @@ EXPORT_SYMBOL_GPL(dpm_suspend_start);
void __suspend_report_result(const char *function, struct device *dev, void *fn, int ret)
{
if (ret)
dev_err(dev, "%s(): %pS returns %d\n", function, fn, ret);
dev_err(dev, "%s(): %ps returns %d\n", function, fn, ret);
}
EXPORT_SYMBOL_GPL(__suspend_report_result);
......
......@@ -451,16 +451,15 @@ static struct wakeup_source *device_wakeup_detach(struct device *dev)
* Detach the @dev's wakeup source object from it, unregister this wakeup source
* object and destroy it.
*/
int device_wakeup_disable(struct device *dev)
void device_wakeup_disable(struct device *dev)
{
struct wakeup_source *ws;
if (!dev || !dev->power.can_wakeup)
return -EINVAL;
return;
ws = device_wakeup_detach(dev);
wakeup_source_unregister(ws);
return 0;
}
EXPORT_SYMBOL_GPL(device_wakeup_disable);
......@@ -502,7 +501,11 @@ EXPORT_SYMBOL_GPL(device_set_wakeup_capable);
*/
int device_set_wakeup_enable(struct device *dev, bool enable)
{
return enable ? device_wakeup_enable(dev) : device_wakeup_disable(dev);
if (enable)
return device_wakeup_enable(dev);
device_wakeup_disable(dev);
return 0;
}
EXPORT_SYMBOL_GPL(device_set_wakeup_enable);
......
......@@ -59,15 +59,14 @@ static int kirkwood_cpuidle_probe(struct platform_device *pdev)
return cpuidle_register(&kirkwood_idle_driver, NULL);
}
static int kirkwood_cpuidle_remove(struct platform_device *pdev)
static void kirkwood_cpuidle_remove(struct platform_device *pdev)
{
cpuidle_unregister(&kirkwood_idle_driver);
return 0;
}
static struct platform_driver kirkwood_cpuidle_driver = {
.probe = kirkwood_cpuidle_probe,
.remove = kirkwood_cpuidle_remove,
.remove_new = kirkwood_cpuidle_remove,
.driver = {
.name = "kirkwood_cpuidle",
},
......
......@@ -44,6 +44,7 @@ static DEFINE_PER_CPU(struct ladder_device, ladder_devices);
/**
* ladder_do_selection - prepares private data for a state change
* @dev: the CPU
* @ldev: the ladder device
* @old_idx: the current state index
* @new_idx: the new target state index
......
......@@ -63,7 +63,7 @@ static int sdhci_pci_init_wakeup(struct sdhci_pci_chip *chip)
if ((pm_flags & MMC_PM_KEEP_POWER) && (pm_flags & MMC_PM_WAKE_SDIO_IRQ))
return device_wakeup_enable(&chip->pdev->dev);
else if (!cap_cd_wake)
return device_wakeup_disable(&chip->pdev->dev);
device_wakeup_disable(&chip->pdev->dev);
return 0;
}
......
......@@ -43,13 +43,11 @@ static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit)
struct dtpm_cpu *dtpm_cpu = to_dtpm_cpu(dtpm);
struct em_perf_domain *pd = em_cpu_get(dtpm_cpu->cpu);
struct em_perf_state *table;
struct cpumask cpus;
unsigned long freq;
u64 power;
int i, nr_cpus;
cpumask_and(&cpus, cpu_online_mask, to_cpumask(pd->cpus));
nr_cpus = cpumask_weight(&cpus);
nr_cpus = cpumask_weight_and(cpu_online_mask, to_cpumask(pd->cpus));
rcu_read_lock();
table = em_perf_state_from_pd(pd);
......@@ -123,11 +121,9 @@ static int update_pd_power_uw(struct dtpm *dtpm)
struct dtpm_cpu *dtpm_cpu = to_dtpm_cpu(dtpm);
struct em_perf_domain *em = em_cpu_get(dtpm_cpu->cpu);
struct em_perf_state *table;
struct cpumask cpus;
int nr_cpus;
cpumask_and(&cpus, cpu_online_mask, to_cpumask(em->cpus));
nr_cpus = cpumask_weight(&cpus);
nr_cpus = cpumask_weight_and(cpu_online_mask, to_cpumask(em->cpus));
rcu_read_lock();
table = em_perf_state_from_pd(em);
......
......@@ -5,27 +5,29 @@
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitmap.h>
#include <linux/cleanup.h>
#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/intel_rapl.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/log2.h>
#include <linux/bitmap.h>
#include <linux/delay.h>
#include <linux/sysfs.h>
#include <linux/cpu.h>
#include <linux/module.h>
#include <linux/nospec.h>
#include <linux/perf_event.h>
#include <linux/platform_device.h>
#include <linux/powercap.h>
#include <linux/suspend.h>
#include <linux/intel_rapl.h>
#include <linux/processor.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/sysfs.h>
#include <linux/types.h>
#include <asm/iosf_mbi.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#include <asm/iosf_mbi.h>
/* bitmasks for RAPL MSRs, used by primitive access functions */
#define ENERGY_STATUS_MASK 0xffffffff
......@@ -1263,6 +1265,7 @@ static const struct x86_cpu_id rapl_ids[] __initconst = {
X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, &rapl_defaults_spr_server),
X86_MATCH_INTEL_FAM6_MODEL(EMERALDRAPIDS_X, &rapl_defaults_spr_server),
X86_MATCH_INTEL_FAM6_MODEL(LUNARLAKE_M, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(ARROWLAKE_H, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(ARROWLAKE, &rapl_defaults_core),
X86_MATCH_INTEL_FAM6_MODEL(LAKEFIELD, &rapl_defaults_core),
......@@ -1506,6 +1509,586 @@ static int rapl_detect_domains(struct rapl_package *rp)
return 0;
}
#ifdef CONFIG_PERF_EVENTS
/*
* Support for RAPL PMU
*
* Register a PMU if any of the registered RAPL Packages have the requirement
* of exposing its energy counters via Perf PMU.
*
* PMU Name:
* power
*
* Events:
* Name Event id RAPL Domain
* energy_cores 0x01 RAPL_DOMAIN_PP0
* energy_pkg 0x02 RAPL_DOMAIN_PACKAGE
* energy_ram 0x03 RAPL_DOMAIN_DRAM
* energy_gpu 0x04 RAPL_DOMAIN_PP1
* energy_psys 0x05 RAPL_DOMAIN_PLATFORM
*
* Unit:
* Joules
*
* Scale:
* 2.3283064365386962890625e-10
* The same RAPL domain in different RAPL Packages may have different
* energy units. Use 2.3283064365386962890625e-10 (2^-32) Joules as
* the fixed unit for all energy counters, and covert each hardware
* counter increase to N times of PMU event counter increases.
*
* This is fully compatible with the current MSR RAPL PMU. This means that
* userspace programs like turbostat can use the same code to handle RAPL Perf
* PMU, no matter what RAPL Interface driver (MSR/TPMI, etc) is running
* underlying on the platform.
*
* Note that RAPL Packages can be probed/removed dynamically, and the events
* supported by each TPMI RAPL device can be different. Thus the RAPL PMU
* support is done on demand, which means
* 1. PMU is registered only if it is needed by a RAPL Package. PMU events for
* unsupported counters are not exposed.
* 2. PMU is unregistered and registered when a new RAPL Package is probed and
* supports new counters that are not supported by current PMU.
* 3. PMU is unregistered when all registered RAPL Packages don't need PMU.
*/
struct rapl_pmu {
struct pmu pmu; /* Perf PMU structure */
u64 timer_ms; /* Maximum expiration time to avoid counter overflow */
unsigned long domain_map; /* Events supported by current registered PMU */
bool registered; /* Whether the PMU has been registered or not */
};
static struct rapl_pmu rapl_pmu;
/* PMU helpers */
static int get_pmu_cpu(struct rapl_package *rp)
{
int cpu;
if (!rp->has_pmu)
return nr_cpu_ids;
/* Only TPMI RAPL is supported for now */
if (rp->priv->type != RAPL_IF_TPMI)
return nr_cpu_ids;
/* TPMI RAPL uses any CPU in the package for PMU */
for_each_online_cpu(cpu)
if (topology_physical_package_id(cpu) == rp->id)
return cpu;
return nr_cpu_ids;
}
static bool is_rp_pmu_cpu(struct rapl_package *rp, int cpu)
{
if (!rp->has_pmu)
return false;
/* Only TPMI RAPL is supported for now */
if (rp->priv->type != RAPL_IF_TPMI)
return false;
/* TPMI RAPL uses any CPU in the package for PMU */
return topology_physical_package_id(cpu) == rp->id;
}
static struct rapl_package_pmu_data *event_to_pmu_data(struct perf_event *event)
{
struct rapl_package *rp = event->pmu_private;
return &rp->pmu_data;
}
/* PMU event callbacks */
static u64 event_read_counter(struct perf_event *event)
{
struct rapl_package *rp = event->pmu_private;
u64 val;
int ret;
/* Return 0 for unsupported events */
if (event->hw.idx < 0)
return 0;
ret = rapl_read_data_raw(&rp->domains[event->hw.idx], ENERGY_COUNTER, false, &val);
/* Return 0 for failed read */
if (ret)
return 0;
return val;
}
static void __rapl_pmu_event_start(struct perf_event *event)
{
struct rapl_package_pmu_data *data = event_to_pmu_data(event);
if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED)))
return;
event->hw.state = 0;
list_add_tail(&event->active_entry, &data->active_list);
local64_set(&event->hw.prev_count, event_read_counter(event));
if (++data->n_active == 1)
hrtimer_start(&data->hrtimer, data->timer_interval,
HRTIMER_MODE_REL_PINNED);
}
static void rapl_pmu_event_start(struct perf_event *event, int mode)
{
struct rapl_package_pmu_data *data = event_to_pmu_data(event);
unsigned long flags;
raw_spin_lock_irqsave(&data->lock, flags);
__rapl_pmu_event_start(event);
raw_spin_unlock_irqrestore(&data->lock, flags);
}
static u64 rapl_event_update(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
struct rapl_package_pmu_data *data = event_to_pmu_data(event);
u64 prev_raw_count, new_raw_count;
s64 delta, sdelta;
/*
* Follow the generic code to drain hwc->prev_count.
* The loop is not expected to run for multiple times.
*/
prev_raw_count = local64_read(&hwc->prev_count);
do {
new_raw_count = event_read_counter(event);
} while (!local64_try_cmpxchg(&hwc->prev_count,
&prev_raw_count, new_raw_count));
/*
* Now we have the new raw value and have updated the prev
* timestamp already. We can now calculate the elapsed delta
* (event-)time and add that to the generic event.
*/
delta = new_raw_count - prev_raw_count;
/*
* Scale delta to smallest unit (2^-32)
* users must then scale back: count * 1/(1e9*2^32) to get Joules
* or use ldexp(count, -32).
* Watts = Joules/Time delta
*/
sdelta = delta * data->scale[event->hw.flags];
local64_add(sdelta, &event->count);
return new_raw_count;
}
static void rapl_pmu_event_stop(struct perf_event *event, int mode)
{
struct rapl_package_pmu_data *data = event_to_pmu_data(event);
struct hw_perf_event *hwc = &event->hw;
unsigned long flags;
raw_spin_lock_irqsave(&data->lock, flags);
/* Mark event as deactivated and stopped */
if (!(hwc->state & PERF_HES_STOPPED)) {
WARN_ON_ONCE(data->n_active <= 0);
if (--data->n_active == 0)
hrtimer_cancel(&data->hrtimer);
list_del(&event->active_entry);
WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
hwc->state |= PERF_HES_STOPPED;
}
/* Check if update of sw counter is necessary */
if ((mode & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
/*
* Drain the remaining delta count out of a event
* that we are disabling:
*/
rapl_event_update(event);
hwc->state |= PERF_HES_UPTODATE;
}
raw_spin_unlock_irqrestore(&data->lock, flags);
}
static int rapl_pmu_event_add(struct perf_event *event, int mode)
{
struct rapl_package_pmu_data *data = event_to_pmu_data(event);
struct hw_perf_event *hwc = &event->hw;
unsigned long flags;
raw_spin_lock_irqsave(&data->lock, flags);
hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
if (mode & PERF_EF_START)
__rapl_pmu_event_start(event);
raw_spin_unlock_irqrestore(&data->lock, flags);
return 0;
}
static void rapl_pmu_event_del(struct perf_event *event, int flags)
{
rapl_pmu_event_stop(event, PERF_EF_UPDATE);
}
/* RAPL PMU event ids, same as shown in sysfs */
enum perf_rapl_events {
PERF_RAPL_PP0 = 1, /* all cores */
PERF_RAPL_PKG, /* entire package */
PERF_RAPL_RAM, /* DRAM */
PERF_RAPL_PP1, /* gpu */
PERF_RAPL_PSYS, /* psys */
PERF_RAPL_MAX
};
#define RAPL_EVENT_MASK GENMASK(7, 0)
static const int event_to_domain[PERF_RAPL_MAX] = {
[PERF_RAPL_PP0] = RAPL_DOMAIN_PP0,
[PERF_RAPL_PKG] = RAPL_DOMAIN_PACKAGE,
[PERF_RAPL_RAM] = RAPL_DOMAIN_DRAM,
[PERF_RAPL_PP1] = RAPL_DOMAIN_PP1,
[PERF_RAPL_PSYS] = RAPL_DOMAIN_PLATFORM,
};
static int rapl_pmu_event_init(struct perf_event *event)
{
struct rapl_package *pos, *rp = NULL;
u64 cfg = event->attr.config & RAPL_EVENT_MASK;
int domain, idx;
/* Only look at RAPL events */
if (event->attr.type != event->pmu->type)
return -ENOENT;
/* Check for supported events only */
if (!cfg || cfg >= PERF_RAPL_MAX)
return -EINVAL;
if (event->cpu < 0)
return -EINVAL;
/* Find out which Package the event belongs to */
list_for_each_entry(pos, &rapl_packages, plist) {
if (is_rp_pmu_cpu(pos, event->cpu)) {
rp = pos;
break;
}
}
if (!rp)
return -ENODEV;
/* Find out which RAPL Domain the event belongs to */
domain = event_to_domain[cfg];
event->event_caps |= PERF_EV_CAP_READ_ACTIVE_PKG;
event->pmu_private = rp; /* Which package */
event->hw.flags = domain; /* Which domain */
event->hw.idx = -1;
/* Find out the index in rp->domains[] to get domain pointer */
for (idx = 0; idx < rp->nr_domains; idx++) {
if (rp->domains[idx].id == domain) {
event->hw.idx = idx;
break;
}
}
return 0;
}
static void rapl_pmu_event_read(struct perf_event *event)
{
rapl_event_update(event);
}
static enum hrtimer_restart rapl_hrtimer_handle(struct hrtimer *hrtimer)
{
struct rapl_package_pmu_data *data =
container_of(hrtimer, struct rapl_package_pmu_data, hrtimer);
struct perf_event *event;
unsigned long flags;
if (!data->n_active)
return HRTIMER_NORESTART;
raw_spin_lock_irqsave(&data->lock, flags);
list_for_each_entry(event, &data->active_list, active_entry)
rapl_event_update(event);
raw_spin_unlock_irqrestore(&data->lock, flags);
hrtimer_forward_now(hrtimer, data->timer_interval);
return HRTIMER_RESTART;
}
/* PMU sysfs attributes */
/*
* There are no default events, but we need to create "events" group (with
* empty attrs) before updating it with detected events.
*/
static struct attribute *attrs_empty[] = {
NULL,
};
static struct attribute_group pmu_events_group = {
.name = "events",
.attrs = attrs_empty,
};
static ssize_t cpumask_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct rapl_package *rp;
cpumask_var_t cpu_mask;
int cpu;
int ret;
if (!alloc_cpumask_var(&cpu_mask, GFP_KERNEL))
return -ENOMEM;
cpus_read_lock();
cpumask_clear(cpu_mask);
/* Choose a cpu for each RAPL Package */
list_for_each_entry(rp, &rapl_packages, plist) {
cpu = get_pmu_cpu(rp);
if (cpu < nr_cpu_ids)
cpumask_set_cpu(cpu, cpu_mask);
}
cpus_read_unlock();
ret = cpumap_print_to_pagebuf(true, buf, cpu_mask);
free_cpumask_var(cpu_mask);
return ret;
}
static DEVICE_ATTR_RO(cpumask);
static struct attribute *pmu_cpumask_attrs[] = {
&dev_attr_cpumask.attr,
NULL
};
static struct attribute_group pmu_cpumask_group = {
.attrs = pmu_cpumask_attrs,
};
PMU_FORMAT_ATTR(event, "config:0-7");
static struct attribute *pmu_format_attr[] = {
&format_attr_event.attr,
NULL
};
static struct attribute_group pmu_format_group = {
.name = "format",
.attrs = pmu_format_attr,
};
static const struct attribute_group *pmu_attr_groups[] = {
&pmu_events_group,
&pmu_cpumask_group,
&pmu_format_group,
NULL
};
#define RAPL_EVENT_ATTR_STR(_name, v, str) \
static struct perf_pmu_events_attr event_attr_##v = { \
.attr = __ATTR(_name, 0444, perf_event_sysfs_show, NULL), \
.event_str = str, \
}
RAPL_EVENT_ATTR_STR(energy-cores, rapl_cores, "event=0x01");
RAPL_EVENT_ATTR_STR(energy-pkg, rapl_pkg, "event=0x02");
RAPL_EVENT_ATTR_STR(energy-ram, rapl_ram, "event=0x03");
RAPL_EVENT_ATTR_STR(energy-gpu, rapl_gpu, "event=0x04");
RAPL_EVENT_ATTR_STR(energy-psys, rapl_psys, "event=0x05");
RAPL_EVENT_ATTR_STR(energy-cores.unit, rapl_unit_cores, "Joules");
RAPL_EVENT_ATTR_STR(energy-pkg.unit, rapl_unit_pkg, "Joules");
RAPL_EVENT_ATTR_STR(energy-ram.unit, rapl_unit_ram, "Joules");
RAPL_EVENT_ATTR_STR(energy-gpu.unit, rapl_unit_gpu, "Joules");
RAPL_EVENT_ATTR_STR(energy-psys.unit, rapl_unit_psys, "Joules");
RAPL_EVENT_ATTR_STR(energy-cores.scale, rapl_scale_cores, "2.3283064365386962890625e-10");
RAPL_EVENT_ATTR_STR(energy-pkg.scale, rapl_scale_pkg, "2.3283064365386962890625e-10");
RAPL_EVENT_ATTR_STR(energy-ram.scale, rapl_scale_ram, "2.3283064365386962890625e-10");
RAPL_EVENT_ATTR_STR(energy-gpu.scale, rapl_scale_gpu, "2.3283064365386962890625e-10");
RAPL_EVENT_ATTR_STR(energy-psys.scale, rapl_scale_psys, "2.3283064365386962890625e-10");
#define RAPL_EVENT_GROUP(_name, domain) \
static struct attribute *pmu_attr_##_name[] = { \
&event_attr_rapl_##_name.attr.attr, \
&event_attr_rapl_unit_##_name.attr.attr, \
&event_attr_rapl_scale_##_name.attr.attr, \
NULL \
}; \
static umode_t is_visible_##_name(struct kobject *kobj, struct attribute *attr, int event) \
{ \
return rapl_pmu.domain_map & BIT(domain) ? attr->mode : 0; \
} \
static struct attribute_group pmu_group_##_name = { \
.name = "events", \
.attrs = pmu_attr_##_name, \
.is_visible = is_visible_##_name, \
}
RAPL_EVENT_GROUP(cores, RAPL_DOMAIN_PP0);
RAPL_EVENT_GROUP(pkg, RAPL_DOMAIN_PACKAGE);
RAPL_EVENT_GROUP(ram, RAPL_DOMAIN_DRAM);
RAPL_EVENT_GROUP(gpu, RAPL_DOMAIN_PP1);
RAPL_EVENT_GROUP(psys, RAPL_DOMAIN_PLATFORM);
static const struct attribute_group *pmu_attr_update[] = {
&pmu_group_cores,
&pmu_group_pkg,
&pmu_group_ram,
&pmu_group_gpu,
&pmu_group_psys,
NULL
};
static int rapl_pmu_update(struct rapl_package *rp)
{
int ret = 0;
/* Return if PMU already covers all events supported by current RAPL Package */
if (rapl_pmu.registered && !(rp->domain_map & (~rapl_pmu.domain_map)))
goto end;
/* Unregister previous registered PMU */
if (rapl_pmu.registered)
perf_pmu_unregister(&rapl_pmu.pmu);
rapl_pmu.registered = false;
rapl_pmu.domain_map |= rp->domain_map;
memset(&rapl_pmu.pmu, 0, sizeof(struct pmu));
rapl_pmu.pmu.attr_groups = pmu_attr_groups;
rapl_pmu.pmu.attr_update = pmu_attr_update;
rapl_pmu.pmu.task_ctx_nr = perf_invalid_context;
rapl_pmu.pmu.event_init = rapl_pmu_event_init;
rapl_pmu.pmu.add = rapl_pmu_event_add;
rapl_pmu.pmu.del = rapl_pmu_event_del;
rapl_pmu.pmu.start = rapl_pmu_event_start;
rapl_pmu.pmu.stop = rapl_pmu_event_stop;
rapl_pmu.pmu.read = rapl_pmu_event_read;
rapl_pmu.pmu.module = THIS_MODULE;
rapl_pmu.pmu.capabilities = PERF_PMU_CAP_NO_EXCLUDE | PERF_PMU_CAP_NO_INTERRUPT;
ret = perf_pmu_register(&rapl_pmu.pmu, "power", -1);
if (ret) {
pr_info("Failed to register PMU\n");
return ret;
}
rapl_pmu.registered = true;
end:
rp->has_pmu = true;
return ret;
}
int rapl_package_add_pmu(struct rapl_package *rp)
{
struct rapl_package_pmu_data *data = &rp->pmu_data;
int idx;
if (rp->has_pmu)
return -EEXIST;
guard(cpus_read_lock)();
for (idx = 0; idx < rp->nr_domains; idx++) {
struct rapl_domain *rd = &rp->domains[idx];
int domain = rd->id;
u64 val;
if (!test_bit(domain, &rp->domain_map))
continue;
/*
* The RAPL PMU granularity is 2^-32 Joules
* data->scale[]: times of 2^-32 Joules for each ENERGY COUNTER increase
*/
val = rd->energy_unit * (1ULL << 32);
do_div(val, ENERGY_UNIT_SCALE * 1000000);
data->scale[domain] = val;
if (!rapl_pmu.timer_ms) {
struct rapl_primitive_info *rpi = get_rpi(rp, ENERGY_COUNTER);
/*
* Calculate the timer rate:
* Use reference of 200W for scaling the timeout to avoid counter
* overflows.
*
* max_count = rpi->mask >> rpi->shift + 1
* max_energy_pj = max_count * rd->energy_unit
* max_time_sec = (max_energy_pj / 1000000000) / 200w
*
* rapl_pmu.timer_ms = max_time_sec * 1000 / 2
*/
val = (rpi->mask >> rpi->shift) + 1;
val *= rd->energy_unit;
do_div(val, 1000000 * 200 * 2);
rapl_pmu.timer_ms = val;
pr_debug("%llu ms overflow timer\n", rapl_pmu.timer_ms);
}
pr_debug("Domain %s: hw unit %lld * 2^-32 Joules\n", rd->name, data->scale[domain]);
}
/* Initialize per package PMU data */
raw_spin_lock_init(&data->lock);
INIT_LIST_HEAD(&data->active_list);
data->timer_interval = ms_to_ktime(rapl_pmu.timer_ms);
hrtimer_init(&data->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
data->hrtimer.function = rapl_hrtimer_handle;
return rapl_pmu_update(rp);
}
EXPORT_SYMBOL_GPL(rapl_package_add_pmu);
void rapl_package_remove_pmu(struct rapl_package *rp)
{
struct rapl_package *pos;
if (!rp->has_pmu)
return;
guard(cpus_read_lock)();
list_for_each_entry(pos, &rapl_packages, plist) {
/* PMU is still needed */
if (pos->has_pmu && pos != rp)
return;
}
perf_pmu_unregister(&rapl_pmu.pmu);
memset(&rapl_pmu, 0, sizeof(struct rapl_pmu));
}
EXPORT_SYMBOL_GPL(rapl_package_remove_pmu);
#endif
/* called from CPU hotplug notifier, hotplug lock held */
void rapl_remove_package_cpuslocked(struct rapl_package *rp)
{
......
......@@ -302,6 +302,8 @@ static int intel_rapl_tpmi_probe(struct auxiliary_device *auxdev,
goto err;
}
rapl_package_add_pmu(trp->rp);
auxiliary_set_drvdata(auxdev, trp);
return 0;
......@@ -314,6 +316,7 @@ static void intel_rapl_tpmi_remove(struct auxiliary_device *auxdev)
{
struct tpmi_rapl_package *trp = auxiliary_get_drvdata(auxdev);
rapl_package_remove_pmu(trp->rp);
rapl_remove_package(trp->rp);
trp_release(trp);
}
......
......@@ -158,6 +158,26 @@ struct rapl_if_priv {
void *rpi;
};
#ifdef CONFIG_PERF_EVENTS
/**
* struct rapl_package_pmu_data: Per package data for PMU support
* @scale: Scale of 2^-32 Joules for each energy counter increase.
* @lock: Lock to protect n_active and active_list.
* @n_active: Number of active events.
* @active_list: List of active events.
* @timer_interval: Maximum timer expiration time before counter overflow.
* @hrtimer: Periodically update the counter to prevent overflow.
*/
struct rapl_package_pmu_data {
u64 scale[RAPL_DOMAIN_MAX];
raw_spinlock_t lock;
int n_active;
struct list_head active_list;
ktime_t timer_interval;
struct hrtimer hrtimer;
};
#endif
/* maximum rapl package domain name: package-%d-die-%d */
#define PACKAGE_DOMAIN_NAME_LENGTH 30
......@@ -176,6 +196,10 @@ struct rapl_package {
struct cpumask cpumask;
char name[PACKAGE_DOMAIN_NAME_LENGTH];
struct rapl_if_priv *priv;
#ifdef CONFIG_PERF_EVENTS
bool has_pmu;
struct rapl_package_pmu_data pmu_data;
#endif
};
struct rapl_package *rapl_find_package_domain_cpuslocked(int id, struct rapl_if_priv *priv,
......@@ -188,4 +212,12 @@ struct rapl_package *rapl_find_package_domain(int id, struct rapl_if_priv *priv,
struct rapl_package *rapl_add_package(int id, struct rapl_if_priv *priv, bool id_is_cpu);
void rapl_remove_package(struct rapl_package *rp);
#ifdef CONFIG_PERF_EVENTS
int rapl_package_add_pmu(struct rapl_package *rp);
void rapl_package_remove_pmu(struct rapl_package *rp);
#else
static inline int rapl_package_add_pmu(struct rapl_package *rp) { return 0; }
static inline void rapl_package_remove_pmu(struct rapl_package *rp) { }
#endif
#endif /* __INTEL_RAPL_H__ */
......@@ -107,7 +107,7 @@ extern void wakeup_sources_read_unlock(int idx);
extern struct wakeup_source *wakeup_sources_walk_start(void);
extern struct wakeup_source *wakeup_sources_walk_next(struct wakeup_source *ws);
extern int device_wakeup_enable(struct device *dev);
extern int device_wakeup_disable(struct device *dev);
extern void device_wakeup_disable(struct device *dev);
extern void device_set_wakeup_capable(struct device *dev, bool capable);
extern int device_set_wakeup_enable(struct device *dev, bool enable);
extern void __pm_stay_awake(struct wakeup_source *ws);
......@@ -154,10 +154,9 @@ static inline int device_wakeup_enable(struct device *dev)
return 0;
}
static inline int device_wakeup_disable(struct device *dev)
static inline void device_wakeup_disable(struct device *dev)
{
dev->power.should_wakeup = false;
return 0;
}
static inline int device_set_wakeup_enable(struct device *dev, bool enable)
......@@ -235,11 +234,10 @@ static inline int device_init_wakeup(struct device *dev, bool enable)
if (enable) {
device_set_wakeup_capable(dev, true);
return device_wakeup_enable(dev);
} else {
device_wakeup_disable(dev);
device_set_wakeup_capable(dev, false);
return 0;
}
device_wakeup_disable(dev);
device_set_wakeup_capable(dev, false);
return 0;
}
#endif /* _LINUX_PM_WAKEUP_H */
......@@ -1361,7 +1361,7 @@ static int __init resume_setup(char *str)
if (noresume)
return 1;
strncpy(resume_file, str, 255);
strscpy(resume_file, str);
return 1;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment