Re: [PATCH V4 3/4] pmdomain: core: Fix debugfs node creation failure

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 





On 10/28/24 18:58, Ulf Hansson wrote:
On Wed, 23 Oct 2024 at 12:22, Sibi Sankar <quic_sibis@xxxxxxxxxxx> wrote:

The domain attributes returned by the perf protocol can end up
reporting identical names across domains, resulting in debugfs
node creation failure. Fix this failure by ensuring that pm domains
get a unique name using ida in pm_genpd_init.

Logs: [X1E reports 'NCC' for all its scmi perf domains]
debugfs: Directory 'NCC' with parent 'pm_genpd' already present!
debugfs: Directory 'NCC' with parent 'pm_genpd' already present!

Reported-by: Johan Hovold <johan+linaro@xxxxxxxxxx>
Closes: https://lore.kernel.org/lkml/ZoQjAWse2YxwyRJv@xxxxxxxxxxxxxxxxxxxx/
Fixes: 718072ceb211 ("PM: domains: create debugfs nodes when adding power domains")
Suggested-by: Ulf Hansson <ulf.hansson@xxxxxxxxxx>
Signed-off-by: Sibi Sankar <quic_sibis@xxxxxxxxxxx>
---

v3:
* Update device names only when a name collision occurs [Dmitry/Ulf]
* Drop Johan's T-b from "fix debugfs node creation failure"

  drivers/pmdomain/core.c   | 65 ++++++++++++++++++++++++++++++---------
  include/linux/pm_domain.h |  1 +
  2 files changed, 51 insertions(+), 15 deletions(-)

diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
index 76490f0bf1e2..756ed0975788 100644
--- a/drivers/pmdomain/core.c
+++ b/drivers/pmdomain/core.c
@@ -7,6 +7,7 @@
  #define pr_fmt(fmt) "PM: " fmt

  #include <linux/delay.h>
+#include <linux/idr.h>
  #include <linux/kernel.h>
  #include <linux/io.h>
  #include <linux/platform_device.h>
@@ -23,6 +24,9 @@
  #include <linux/cpu.h>
  #include <linux/debugfs.h>

+/* Provides a unique ID for each genpd device */
+static DEFINE_IDA(genpd_ida);
+
  #define GENPD_RETRY_MAX_MS     250             /* Approximate */

  #define GENPD_DEV_CALLBACK(genpd, type, callback, dev)         \
@@ -189,7 +193,7 @@ static inline bool irq_safe_dev_in_sleep_domain(struct device *dev,

         if (ret)
                 dev_warn_once(dev, "PM domain %s will not be powered off\n",
-                               genpd->name);
+                             dev_name(&genpd->dev));

         return ret;
  }
@@ -274,7 +278,7 @@ static void genpd_debug_remove(struct generic_pm_domain *genpd)
         if (!genpd_debugfs_dir)
                 return;

-       debugfs_lookup_and_remove(genpd->name, genpd_debugfs_dir);
+       debugfs_lookup_and_remove(dev_name(&genpd->dev), genpd_debugfs_dir);
  }

  static void genpd_update_accounting(struct generic_pm_domain *genpd)
@@ -731,7 +735,7 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
         genpd->states[state_idx].power_on_latency_ns = elapsed_ns;
         genpd->gd->max_off_time_changed = true;
         pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
-                genpd->name, "on", elapsed_ns);
+                dev_name(&genpd->dev), "on", elapsed_ns);

  out:
         raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_ON, NULL);
@@ -782,7 +786,7 @@ static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed)
         genpd->states[state_idx].power_off_latency_ns = elapsed_ns;
         genpd->gd->max_off_time_changed = true;
         pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
-                genpd->name, "off", elapsed_ns);
+                dev_name(&genpd->dev), "off", elapsed_ns);

  out:
         raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_OFF,
@@ -1941,7 +1945,7 @@ int dev_pm_genpd_add_notifier(struct device *dev, struct notifier_block *nb)

         if (ret) {
                 dev_warn(dev, "failed to add notifier for PM domain %s\n",
-                        genpd->name);
+                        dev_name(&genpd->dev));
                 return ret;
         }

@@ -1988,7 +1992,7 @@ int dev_pm_genpd_remove_notifier(struct device *dev)

         if (ret) {
                 dev_warn(dev, "failed to remove notifier for PM domain %s\n",
-                        genpd->name);
+                        dev_name(&genpd->dev));
                 return ret;
         }

@@ -2014,7 +2018,7 @@ static int genpd_add_subdomain(struct generic_pm_domain *genpd,
          */
         if (!genpd_is_irq_safe(genpd) && genpd_is_irq_safe(subdomain)) {
                 WARN(1, "Parent %s of subdomain %s must be IRQ safe\n",
-                               genpd->name, subdomain->name);
+                    dev_name(&genpd->dev), subdomain->name);
                 return -EINVAL;
         }

@@ -2089,7 +2093,7 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,

         if (!list_empty(&subdomain->parent_links) || subdomain->device_count) {
                 pr_warn("%s: unable to remove subdomain %s\n",
-                       genpd->name, subdomain->name);
+                       dev_name(&genpd->dev), subdomain->name);
                 ret = -EBUSY;
                 goto out;
         }
@@ -2199,6 +2203,23 @@ static void genpd_lock_init(struct generic_pm_domain *genpd)
         }
  }

+static bool genpd_name_present(const char *name)
+{
+       bool ret = false;
+       const struct generic_pm_domain *gpd;
+
+       mutex_lock(&gpd_list_lock);
+       list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
+               if (!strcmp(dev_name(&gpd->dev), name)) {
+                       ret = true;
+                       break;
+               }
+       }
+       mutex_unlock(&gpd_list_lock);
+
+       return ret;
+}
+
  /**
   * pm_genpd_init - Initialize a generic I/O PM domain object.
   * @genpd: PM domain object to initialize.
@@ -2226,6 +2247,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
         genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON;
         genpd->device_count = 0;
         genpd->provider = NULL;
+       genpd->device_id = -ENXIO;
         genpd->has_provider = false;
         genpd->accounting_time = ktime_get_mono_fast_ns();
         genpd->domain.ops.runtime_suspend = genpd_runtime_suspend;
@@ -2266,7 +2288,18 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
                 return ret;

         device_initialize(&genpd->dev);
-       dev_set_name(&genpd->dev, "%s", genpd->name);
+
+       if (!genpd_name_present(genpd->name)) {
+               dev_set_name(&genpd->dev, "%s", genpd->name);
+       } else {
+               ret = ida_alloc(&genpd_ida, GFP_KERNEL);
+               if (ret < 0) {
+                       put_device(&genpd->dev);
+                       return ret;
+               }
+               genpd->device_id = ret;
+               dev_set_name(&genpd->dev, "%s_%u", genpd->name, genpd->device_id);
+       }

If we can't assume that the genpd->name is unique, I think we need to
hold the gpd_list_lock over this entire new section, until we have
added the new genpd in the gpd_list. I am not sure we really want this
as it could hurt (theoretically at least) boot/probing on systems
where a lot of genpds are being used.

That said, I would suggest we go for Dmitry's suggestion to make this
genpd provider specific. Let's add a new genpd flag that genpd
providers can set, if they need an ida to be tagged on to their
device-name. Then we should set that flag for SCMI perf/power domains.

lol, will do ^^ in the next re-spin.

-Sibi


[...]

Kind regards
Uffe




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [Linux for Sparc]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux