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

Merge back earlier power capping changes for v6.6.

parents 16e95a62 2fa00769
Loading
Loading
Loading
Loading
+92 −67
Original line number Diff line number Diff line
@@ -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;
	unsigned int sp = 0, reg_zones = 0;
	struct scmi_powercap_zone *spz, **zones_stack;

	if (!spz->info)
		return ret;
	zones_stack = kcalloc(pr->num_zones, sizeof(spz), GFP_KERNEL);
	if (!zones_stack)
		return -ENOMEM;

	spz = list_first_entry_or_null(&pr->scmi_zones,
				       struct scmi_powercap_zone, node);
	while (spz) {
		struct scmi_powercap_zone *parent;

		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 && !parent->registered) {
			zones_stack[sp++] = spz;
			spz = parent;
		} else {
			ret = scmi_powercap_register_zone(pr, spz, parent);
			if (!ret) {
				reg_zones++;
			} else if (sp) {
				/* Failed to register a non-leaf zone.
				 * Bail-out.
				 */
		if (!parent->info)
			return -ENODEV;

		ret = scmi_powercap_register_zone(pr, parent);
		if (ret)
			return ret;
				dev_err(dev,
					"Failed to register non-leaf zone - ret:%d\n",
					ret);
				scmi_powercap_unregister_all_zones(pr);
				reg_zones = 0;
				goto out;
			}

	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;
		} 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);
			/* 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);
	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;
}

+1 −1
Original line number Diff line number Diff line
@@ -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;