diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-05-21 11:40:49 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-05-21 11:40:49 -0700 |
commit | 98f312bc58156a5922b5a496119e3a42b0ad439a (patch) | |
tree | 6a3b22ef591871c42f9499019cd4cef7b7730285 | |
parent | 62a12816cb2006776ac8ee9ef026fd99c3a47de6 (diff) | |
parent | dee8f20e61aea655a43b74e5b65bcc6fbc69df7b (diff) | |
download | lwn-98f312bc58156a5922b5a496119e3a42b0ad439a.tar.gz lwn-98f312bc58156a5922b5a496119e3a42b0ad439a.zip |
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
-rw-r--r-- | drivers/cpufreq/amd-pstate.c | 7 | ||||
-rw-r--r-- | drivers/opp/core.c | 31 | ||||
-rw-r--r-- | drivers/pmdomain/core.c | 10 | ||||
-rw-r--r-- | include/linux/pm_domain.h | 6 |
4 files changed, 53 insertions, 1 deletions
diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 6a342b0c0140..1b7e82a0ad2e 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -1441,6 +1441,13 @@ free_cpudata1: 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; } diff --git a/drivers/opp/core.c b/drivers/opp/core.c index e233734b7220..cb4611fe1b5b 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -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; @@ -2428,6 +2429,34 @@ static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev, } /* + * 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. * diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c index 342779464c0d..623d15b68707 100644 --- a/drivers/pmdomain/core.c +++ b/drivers/pmdomain/core.c @@ -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) { diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 772d3280d35f..f24546a3d3db 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -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) { |