summaryrefslogtreecommitdiff
path: root/drivers/powercap
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-08-28 18:04:39 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2023-08-28 18:04:39 -0700
commitccc5e9817719f59b3dea7b7a168861b4bf0b4ff4 (patch)
tree42ee50746bab5720ce251485444e84dc4888e2f6 /drivers/powercap
parent330235e87410349042468b52baff02af7cb7d331 (diff)
parent422ec6fe2704593c87c14a8ef296d92f6b368c53 (diff)
Merge tag 'pm-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull power management updates from Rafael Wysocki: "These rework cpuidle governors to call tick_nohz_get_sleep_length() less often and fix one of them, rework hibernation to avoid storing pages filled with zeros in hibernation images, switch over some cpufreq drivers to use void remove callbacks, fix and clean up multiple cpufreq drivers, fix the devfreq core, update the cpupower utility and make other assorted improvements. Specifics: - Rework the menu and teo cpuidle governors to avoid calling tick_nohz_get_sleep_length(), which is likely to become quite expensive going forward, too often and improve making decisions regarding whether or not to stop the scheduler tick in the teo governor (Rafael Wysocki) - Improve the performance of cpufreq_stats_create_table() in some cases (Liao Chang) - Fix two issues in the amd-pstate-ut cpufreq driver (Swapnil Sapkal) - Use clamp() helper macro to improve the code readability in cpufreq_verify_within_limits() (Liao Chang) - Set stale CPU frequency to minimum in intel_pstate (Doug Smythies) - Migrate cpufreq drivers for various platforms to use void remove callback (Yangtao Li) - Add online/offline/exit hooks for Tegra driver (Sumit Gupta) - Explicitly include correct DT includes in cpufreq (Rob Herring) - Frequency domain updates for qcom-hw driver (Neil Armstrong) - Modify AMD pstate driver return the highest_perf value (Meng Li) - Generic cleanups for cppc, mediatek and powernow driver (Liao Chang, Konrad Dybcio) - Add more platforms to cpufreq-arm driver's blocklist (AngeloGioacchino Del Regno and Konrad Dybcio) - brcmstb-avs-cpufreq: Fix -Warray-bounds bug (Gustavo A. R. Silva) - Add device PM helpers to allow a device to remain powered-on during system-wide transitions (Ulf Hansson) - Rework hibernation memory snapshotting to avoid storing pages filled with zeros in hibernation image files (Brian Geffon) - Add check to make sure that CPU latency QoS constraints do not use negative values (Clive Lin) - Optimize rp->domains memory allocation in the Intel RAPL power capping driver (xiongxin) - Remove recursion while parsing zones in the arm_scmi power capping driver (Cristian Marussi) - Fix memory leak in devfreq_dev_release() (Boris Brezillon) - Rewrite devfreq_monitor_start() kerneldoc comment (Manivannan Sadhasivam) - Explicitly include correct DT includes in devfreq (Rob Herring) - Remove unsued pm_runtime_update_max_time_suspended() extern declaration (YueHaibing) - Add turbo-boost support to cpupower (Wyes Karny) - Add support for amd_pstate mode change to cpupower (Wyes Karny) - Fix 'cpupower idle_set' command to accept only numeric values of arguments (Likhitha Korrapati) - Clean up OPP code and add new frequency related APIs to it (Viresh Kumar, Manivannan Sadhasivam) - Convert ti cpufreq/opp bindings to json schema (Nishanth Menon)" * tag 'pm-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (74 commits) cpufreq: tegra194: remove opp table in exit hook cpufreq: powernow-k8: Use related_cpus instead of cpus in driver.exit() cpufreq: tegra194: add online/offline hooks cpuidle: teo: Avoid unnecessary variable assignments cpufreq: qcom-cpufreq-hw: add support for 4 freq domains dt-bindings: cpufreq: qcom-hw: add a 4th frequency domain cpufreq: amd-pstate-ut: Fix kernel panic when loading the driver cpufreq: amd-pstate-ut: Remove module parameter access cpufreq: Use clamp() helper macro to improve the code readability PM: sleep: Add helpers to allow a device to remain powered-on PM: QoS: Add check to make sure CPU latency is non-negative PM: runtime: Remove unsued extern declaration of pm_runtime_update_max_time_suspended() cpufreq: intel_pstate: set stale CPU frequency to minimum cpufreq: stats: Improve the performance of cpufreq_stats_create_table() dt-bindings: cpufreq: Convert ti-cpufreq to json schema dt-bindings: opp: Convert ti-omap5-opp-supply to json schema OPP: Fix argument name in doc comment cpuidle: menu: Skip tick_nohz_get_sleep_length() call in some cases cpufreq: cppc: Set fie_disabled to FIE_DISABLED if fails to create kworker_fie cpufreq: cppc: cppc_cpufreq_get_rate() returns zero in all error cases. ...
Diffstat (limited to 'drivers/powercap')
-rw-r--r--drivers/powercap/arm_scmi_powercap.c159
-rw-r--r--drivers/powercap/intel_rapl_common.c2
2 files changed, 93 insertions, 68 deletions
diff --git a/drivers/powercap/arm_scmi_powercap.c b/drivers/powercap/arm_scmi_powercap.c
index 5231f6d52ae3..a081f177e702 100644
--- a/drivers/powercap/arm_scmi_powercap.c
+++ b/drivers/powercap/arm_scmi_powercap.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/powercap.h>
#include <linux/scmi_protocol.h>
+#include <linux/slab.h>
#define to_scmi_powercap_zone(z) \
container_of(z, struct scmi_powercap_zone, zone)
@@ -19,6 +20,8 @@
static const struct scmi_powercap_proto_ops *powercap_ops;
struct scmi_powercap_zone {
+ bool registered;
+ bool invalid;
unsigned int height;
struct device *dev;
struct scmi_protocol_handle *ph;
@@ -32,6 +35,7 @@ struct scmi_powercap_root {
unsigned int num_zones;
struct scmi_powercap_zone *spzones;
struct list_head *registered_zones;
+ struct list_head scmi_zones;
};
static struct powercap_control_type *scmi_top_pcntrl;
@@ -271,12 +275,6 @@ static void scmi_powercap_unregister_all_zones(struct scmi_powercap_root *pr)
}
}
-static inline bool
-scmi_powercap_is_zone_registered(struct scmi_powercap_zone *spz)
-{
- return !list_empty(&spz->node);
-}
-
static inline unsigned int
scmi_powercap_get_zone_height(struct scmi_powercap_zone *spz)
{
@@ -295,11 +293,46 @@ scmi_powercap_get_parent_zone(struct scmi_powercap_zone *spz)
return &spz->spzones[spz->info->parent_id];
}
+static int scmi_powercap_register_zone(struct scmi_powercap_root *pr,
+ struct scmi_powercap_zone *spz,
+ struct scmi_powercap_zone *parent)
+{
+ int ret = 0;
+ struct powercap_zone *z;
+
+ if (spz->invalid) {
+ list_del(&spz->node);
+ return -EINVAL;
+ }
+
+ z = powercap_register_zone(&spz->zone, scmi_top_pcntrl, spz->info->name,
+ parent ? &parent->zone : NULL,
+ &zone_ops, 1, &constraint_ops);
+ if (!IS_ERR(z)) {
+ spz->height = scmi_powercap_get_zone_height(spz);
+ spz->registered = true;
+ list_move(&spz->node, &pr->registered_zones[spz->height]);
+ dev_dbg(spz->dev, "Registered node %s - parent %s - height:%d\n",
+ spz->info->name, parent ? parent->info->name : "ROOT",
+ spz->height);
+ } else {
+ list_del(&spz->node);
+ ret = PTR_ERR(z);
+ dev_err(spz->dev,
+ "Error registering node:%s - parent:%s - h:%d - ret:%d\n",
+ spz->info->name,
+ parent ? parent->info->name : "ROOT",
+ spz->height, ret);
+ }
+
+ return ret;
+}
+
/**
- * scmi_powercap_register_zone - Register an SCMI powercap zone recursively
+ * scmi_zones_register- Register SCMI powercap zones starting from parent zones
*
+ * @dev: A reference to the SCMI device
* @pr: A reference to the root powercap zones descriptors
- * @spz: A reference to the SCMI powercap zone to register
*
* When registering SCMI powercap zones with the powercap framework we should
* take care to always register zones starting from the root ones and to
@@ -309,10 +342,10 @@ scmi_powercap_get_parent_zone(struct scmi_powercap_zone *spz)
* zones provided by the SCMI platform firmware is built to comply with such
* requirement.
*
- * This function, given an SCMI powercap zone to register, takes care to walk
- * the SCMI powercap zones tree up to the root looking recursively for
- * unregistered parent zones before registering the provided zone; at the same
- * time each registered zone height in such a tree is accounted for and each
+ * This function, given the set of SCMI powercap zones to register, takes care
+ * to walk the SCMI powercap zones trees up to the root registering any
+ * unregistered parent zone before registering the child zones; at the same
+ * time each registered-zone height in such a tree is accounted for and each
* zone, once registered, is stored in the @registered_zones array that is
* indexed by zone height: this way will be trivial, at unregister time, to walk
* the @registered_zones array backward and unregister all the zones starting
@@ -330,57 +363,55 @@ scmi_powercap_get_parent_zone(struct scmi_powercap_zone *spz)
*
* Return: 0 on Success
*/
-static int scmi_powercap_register_zone(struct scmi_powercap_root *pr,
- struct scmi_powercap_zone *spz)
+static int scmi_zones_register(struct device *dev,
+ struct scmi_powercap_root *pr)
{
int ret = 0;
- struct scmi_powercap_zone *parent;
-
- if (!spz->info)
- return ret;
+ unsigned int sp = 0, reg_zones = 0;
+ struct scmi_powercap_zone *spz, **zones_stack;
- parent = scmi_powercap_get_parent_zone(spz);
- if (parent && !scmi_powercap_is_zone_registered(parent)) {
- /*
- * Bail out if a parent domain was marked as unsupported:
- * only domains participating as leaves can be skipped.
- */
- if (!parent->info)
- return -ENODEV;
+ zones_stack = kcalloc(pr->num_zones, sizeof(spz), GFP_KERNEL);
+ if (!zones_stack)
+ return -ENOMEM;
- ret = scmi_powercap_register_zone(pr, parent);
- if (ret)
- return ret;
- }
+ spz = list_first_entry_or_null(&pr->scmi_zones,
+ struct scmi_powercap_zone, node);
+ while (spz) {
+ struct scmi_powercap_zone *parent;
- if (!scmi_powercap_is_zone_registered(spz)) {
- struct powercap_zone *z;
-
- z = powercap_register_zone(&spz->zone,
- scmi_top_pcntrl,
- spz->info->name,
- parent ? &parent->zone : NULL,
- &zone_ops, 1, &constraint_ops);
- if (!IS_ERR(z)) {
- spz->height = scmi_powercap_get_zone_height(spz);
- list_add(&spz->node,
- &pr->registered_zones[spz->height]);
- dev_dbg(spz->dev,
- "Registered node %s - parent %s - height:%d\n",
- spz->info->name,
- parent ? parent->info->name : "ROOT",
- spz->height);
- ret = 0;
+ parent = scmi_powercap_get_parent_zone(spz);
+ if (parent && !parent->registered) {
+ zones_stack[sp++] = spz;
+ spz = parent;
} else {
- ret = PTR_ERR(z);
- dev_err(spz->dev,
- "Error registering node:%s - parent:%s - h:%d - ret:%d\n",
- spz->info->name,
- parent ? parent->info->name : "ROOT",
- spz->height, ret);
+ ret = scmi_powercap_register_zone(pr, spz, parent);
+ if (!ret) {
+ reg_zones++;
+ } else if (sp) {
+ /* Failed to register a non-leaf zone.
+ * Bail-out.
+ */
+ dev_err(dev,
+ "Failed to register non-leaf zone - ret:%d\n",
+ ret);
+ scmi_powercap_unregister_all_zones(pr);
+ reg_zones = 0;
+ goto out;
+ }
+ /* Pick next zone to process */
+ if (sp)
+ spz = zones_stack[--sp];
+ else
+ spz = list_first_entry_or_null(&pr->scmi_zones,
+ struct scmi_powercap_zone,
+ node);
}
}
+out:
+ kfree(zones_stack);
+ dev_info(dev, "Registered %d SCMI Powercap domains !\n", reg_zones);
+
return ret;
}
@@ -424,6 +455,8 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
if (!pr->registered_zones)
return -ENOMEM;
+ INIT_LIST_HEAD(&pr->scmi_zones);
+
for (i = 0, spz = pr->spzones; i < pr->num_zones; i++, spz++) {
/*
* Powercap domains are validate by the protocol layer, i.e.
@@ -438,6 +471,7 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
INIT_LIST_HEAD(&spz->node);
INIT_LIST_HEAD(&pr->registered_zones[i]);
+ list_add_tail(&spz->node, &pr->scmi_zones);
/*
* Forcibly skip powercap domains using an abstract scale.
* Note that only leaves domains can be skipped, so this could
@@ -448,7 +482,7 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
dev_warn(dev,
"Abstract power scale not supported. Skip %s.\n",
spz->info->name);
- spz->info = NULL;
+ spz->invalid = true;
continue;
}
}
@@ -457,21 +491,12 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
* Scan array of retrieved SCMI powercap domains and register them
* recursively starting from the root domains.
*/
- for (i = 0, spz = pr->spzones; i < pr->num_zones; i++, spz++) {
- ret = scmi_powercap_register_zone(pr, spz);
- if (ret) {
- dev_err(dev,
- "Failed to register powercap zone %s - ret:%d\n",
- spz->info->name, ret);
- scmi_powercap_unregister_all_zones(pr);
- return ret;
- }
- }
+ ret = scmi_zones_register(dev, pr);
+ if (ret)
+ return ret;
dev_set_drvdata(dev, pr);
- dev_info(dev, "Registered %d SCMI Powercap domains !\n", pr->num_zones);
-
return ret;
}
diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c
index 72a8d336f980..5c2e6d5eea2a 100644
--- a/drivers/powercap/intel_rapl_common.c
+++ b/drivers/powercap/intel_rapl_common.c
@@ -1485,7 +1485,7 @@ static int rapl_detect_domains(struct rapl_package *rp)
}
pr_debug("found %d domains on %s\n", rp->nr_domains, rp->name);
- rp->domains = kcalloc(rp->nr_domains + 1, sizeof(struct rapl_domain),
+ rp->domains = kcalloc(rp->nr_domains, sizeof(struct rapl_domain),
GFP_KERNEL);
if (!rp->domains)
return -ENOMEM;