Commit 98f312bc authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'pm-6.10-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull power management fixes from Rafael Wysocki:
 "These fix the amd-pstate driver and the operating performance point
  (OPP) handling related to generic PM domains.

  Specifics:

   - Fix a memory leak in the exit path of amd-pstate (Peng Ma)

   - Fix required_opp_tables handling in the cases when multiple generic
     PM domains share one OPP table (Viresh Kumar)"

* tag 'pm-6.10-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  OPP: Fix required_opp_tables for multiple genpds using same table
  cpufreq: amd-pstate: fix memory leak on CPU EPP exit
parents 62a12816 dee8f20e
......@@ -1441,6 +1441,13 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
static int amd_pstate_epp_cpu_exit(struct cpufreq_policy *policy)
{
struct amd_cpudata *cpudata = policy->driver_data;
if (cpudata) {
kfree(cpudata);
policy->driver_data = NULL;
}
pr_debug("CPU %d exiting\n", policy->cpu);
return 0;
}
......
......@@ -2394,7 +2394,8 @@ static void _opp_detach_genpd(struct opp_table *opp_table)
static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev,
const char * const *names, struct device ***virt_devs)
{
struct device *virt_dev;
struct device *virt_dev, *gdev;
struct opp_table *genpd_table;
int index = 0, ret = -EINVAL;
const char * const *name = names;
......@@ -2427,6 +2428,34 @@ static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev,
goto err;
}
/*
* The required_opp_tables parsing is not perfect, as the OPP
* core does the parsing solely based on the DT node pointers.
* The core sets the required_opp_tables entry to the first OPP
* table in the "opp_tables" list, that matches with the node
* pointer.
*
* If the target DT OPP table is used by multiple devices and
* they all create separate instances of 'struct opp_table' from
* it, then it is possible that the required_opp_tables entry
* may be set to the incorrect sibling device.
*
* Cross check it again and fix if required.
*/
gdev = dev_to_genpd_dev(virt_dev);
if (IS_ERR(gdev))
return PTR_ERR(gdev);
genpd_table = _find_opp_table(gdev);
if (!IS_ERR(genpd_table)) {
if (genpd_table != opp_table->required_opp_tables[index]) {
dev_pm_opp_put_opp_table(opp_table->required_opp_tables[index]);
opp_table->required_opp_tables[index] = genpd_table;
} else {
dev_pm_opp_put_opp_table(genpd_table);
}
}
/*
* Add the virtual genpd device as a user of the OPP table, so
* we can call dev_pm_opp_set_opp() on it directly.
......
......@@ -184,6 +184,16 @@ static struct generic_pm_domain *dev_to_genpd(struct device *dev)
return pd_to_genpd(dev->pm_domain);
}
struct device *dev_to_genpd_dev(struct device *dev)
{
struct generic_pm_domain *genpd = dev_to_genpd(dev);
if (IS_ERR(genpd))
return ERR_CAST(genpd);
return &genpd->dev;
}
static int genpd_stop_dev(const struct generic_pm_domain *genpd,
struct device *dev)
{
......
......@@ -260,6 +260,7 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
int pm_genpd_init(struct generic_pm_domain *genpd,
struct dev_power_governor *gov, bool is_off);
int pm_genpd_remove(struct generic_pm_domain *genpd);
struct device *dev_to_genpd_dev(struct device *dev);
int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state);
int dev_pm_genpd_add_notifier(struct device *dev, struct notifier_block *nb);
int dev_pm_genpd_remove_notifier(struct device *dev);
......@@ -307,6 +308,11 @@ static inline int pm_genpd_remove(struct generic_pm_domain *genpd)
return -EOPNOTSUPP;
}
static inline struct device *dev_to_genpd_dev(struct device *dev)
{
return ERR_PTR(-EOPNOTSUPP);
}
static inline int dev_pm_genpd_set_performance_state(struct device *dev,
unsigned int state)
{
......
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