Power management fixes for 6.10-rc1

- 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).
 -----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEE4fcc61cGeeHD/fCwgsRv/nhiVHEFAmZMYk8SHHJqd0Byand5
 c29ja2kubmV0AAoJEILEb/54YlRxdUIP/31SNqWSNni7aiUirnMIg60UbpM3jTMm
 apkqHaSqdpL4ExM/Y8caSL9rIQD3P1GhWHy+0JVuK5tNiFH84oyMmKYd0v01QVy/
 DLDs55XpPAgbkEjCcloe3QaR8mWgnQIXatjQkVoXZu3LAH6QLnDzT5w7C4HFXFEL
 4gVlBvM/S69pf6FBG0SZhKLON3zryGHCBQMqOFcowJ8QGnfQsC+YzEWwvyncsDv4
 nFV/lje/LwARlsxUfjc7EZmf8tRtfgkgSFm+3pI/EtQikKwI0ttheF8eE4MEtpxe
 0A3xCV1aDq+tQEXgU7IIqVvsuu5xOcdfJ3rfYm5GmgZSh1RU1b80S7MJPWBJn0ip
 PTjVZ3UyaW+YhGw5br2uXWK427PRhPHUyfCPO1UiSSDu0qZPNNHIjv5RQk336Sbt
 NQUl7XO45Lv5XwGq8uqCXxnxN8LaNiOd+JZ6xm3R34Pc/vR6zEptOc/748WS2cxV
 O2h1F6u0MiFqP9ieTcY23anrZqU//a61P8xz9e6oRfieUylOExG/aAsNrZ9Q2J6Z
 T9mMj9OQlcbXIu7X2Ai6Fn3ppWR8/rVJu2i2WwrsGYkdnWuJ3BqdMOpnKw1438bb
 2AC2Pc0hC22p8/tpqckcwSHJOOYCynOc7y/2/X7skcY+jkXg5zhPtBCx3GhCsEjV
 RUj9L2CTorvS
 =mGfT
 -----END PGP SIGNATURE-----

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
This commit is contained in:
Linus Torvalds 2024-05-21 11:40:49 -07:00
commit 98f312bc58
4 changed files with 53 additions and 1 deletions

View file

@ -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;
}

View file

@ -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.

View file

@ -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)
{

View file

@ -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)
{