Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

pmdomain: core: Add GENPD_FLAG_DEV_NAME_FW flag

Introduce GENPD_FLAG_DEV_NAME_FW flag which instructs genpd to generate
an unique device name using ida. It is aimed to be used by genpd providers
which derive their names directly from FW making them susceptible to
debugfs node creation failures.

Reported-by: Johan Hovold <johan+linaro@kernel.org>
Closes: https://lore.kernel.org/lkml/ZoQjAWse2YxwyRJv@hovoldconsulting.com/
Fixes: 718072ceb211 ("PM: domains: create debugfs nodes when adding power domains")
Suggested-by: Ulf Hansson <ulf.hansson@linaro.org>
Suggested-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com>
Cc: stable@vger.kernel.org
Message-ID: <20241030125512.2884761-5-quic_sibis@quicinc.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

authored by

Sibi Sankar and committed by
Ulf Hansson
899f4453 77385688

+40 -15
+34 -15
drivers/pmdomain/core.c
··· 7 7 #define pr_fmt(fmt) "PM: " fmt 8 8 9 9 #include <linux/delay.h> 10 + #include <linux/idr.h> 10 11 #include <linux/kernel.h> 11 12 #include <linux/io.h> 12 13 #include <linux/platform_device.h> ··· 23 22 #include <linux/export.h> 24 23 #include <linux/cpu.h> 25 24 #include <linux/debugfs.h> 25 + 26 + /* Provides a unique ID for each genpd device */ 27 + static DEFINE_IDA(genpd_ida); 26 28 27 29 #define GENPD_RETRY_MAX_MS 250 /* Approximate */ 28 30 ··· 175 171 #define genpd_is_cpu_domain(genpd) (genpd->flags & GENPD_FLAG_CPU_DOMAIN) 176 172 #define genpd_is_rpm_always_on(genpd) (genpd->flags & GENPD_FLAG_RPM_ALWAYS_ON) 177 173 #define genpd_is_opp_table_fw(genpd) (genpd->flags & GENPD_FLAG_OPP_TABLE_FW) 174 + #define genpd_is_dev_name_fw(genpd) (genpd->flags & GENPD_FLAG_DEV_NAME_FW) 178 175 179 176 static inline bool irq_safe_dev_in_sleep_domain(struct device *dev, 180 177 const struct generic_pm_domain *genpd) ··· 194 189 195 190 if (ret) 196 191 dev_warn_once(dev, "PM domain %s will not be powered off\n", 197 - genpd->name); 192 + dev_name(&genpd->dev)); 198 193 199 194 return ret; 200 195 } ··· 279 274 if (!genpd_debugfs_dir) 280 275 return; 281 276 282 - debugfs_lookup_and_remove(genpd->name, genpd_debugfs_dir); 277 + debugfs_lookup_and_remove(dev_name(&genpd->dev), genpd_debugfs_dir); 283 278 } 284 279 285 280 static void genpd_update_accounting(struct generic_pm_domain *genpd) ··· 736 731 genpd->states[state_idx].power_on_latency_ns = elapsed_ns; 737 732 genpd->gd->max_off_time_changed = true; 738 733 pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 739 - genpd->name, "on", elapsed_ns); 734 + dev_name(&genpd->dev), "on", elapsed_ns); 740 735 741 736 out: 742 737 raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_ON, NULL); ··· 787 782 genpd->states[state_idx].power_off_latency_ns = elapsed_ns; 788 783 genpd->gd->max_off_time_changed = true; 789 784 pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", 790 - genpd->name, "off", elapsed_ns); 785 + dev_name(&genpd->dev), "off", elapsed_ns); 791 786 792 787 out: 793 788 raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_OFF, ··· 1945 1940 1946 1941 if (ret) { 1947 1942 dev_warn(dev, "failed to add notifier for PM domain %s\n", 1948 - genpd->name); 1943 + dev_name(&genpd->dev)); 1949 1944 return ret; 1950 1945 } 1951 1946 ··· 1992 1987 1993 1988 if (ret) { 1994 1989 dev_warn(dev, "failed to remove notifier for PM domain %s\n", 1995 - genpd->name); 1990 + dev_name(&genpd->dev)); 1996 1991 return ret; 1997 1992 } 1998 1993 ··· 2018 2013 */ 2019 2014 if (!genpd_is_irq_safe(genpd) && genpd_is_irq_safe(subdomain)) { 2020 2015 WARN(1, "Parent %s of subdomain %s must be IRQ safe\n", 2021 - genpd->name, subdomain->name); 2016 + dev_name(&genpd->dev), subdomain->name); 2022 2017 return -EINVAL; 2023 2018 } 2024 2019 ··· 2093 2088 2094 2089 if (!list_empty(&subdomain->parent_links) || subdomain->device_count) { 2095 2090 pr_warn("%s: unable to remove subdomain %s\n", 2096 - genpd->name, subdomain->name); 2091 + dev_name(&genpd->dev), subdomain->name); 2097 2092 ret = -EBUSY; 2098 2093 goto out; 2099 2094 } ··· 2230 2225 genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON; 2231 2226 genpd->device_count = 0; 2232 2227 genpd->provider = NULL; 2228 + genpd->device_id = -ENXIO; 2233 2229 genpd->has_provider = false; 2234 2230 genpd->accounting_time = ktime_get_mono_fast_ns(); 2235 2231 genpd->domain.ops.runtime_suspend = genpd_runtime_suspend; ··· 2271 2265 return ret; 2272 2266 2273 2267 device_initialize(&genpd->dev); 2274 - dev_set_name(&genpd->dev, "%s", genpd->name); 2268 + 2269 + if (!genpd_is_dev_name_fw(genpd)) { 2270 + dev_set_name(&genpd->dev, "%s", genpd->name); 2271 + } else { 2272 + ret = ida_alloc(&genpd_ida, GFP_KERNEL); 2273 + if (ret < 0) { 2274 + put_device(&genpd->dev); 2275 + return ret; 2276 + } 2277 + genpd->device_id = ret; 2278 + dev_set_name(&genpd->dev, "%s_%u", genpd->name, genpd->device_id); 2279 + } 2275 2280 2276 2281 mutex_lock(&gpd_list_lock); 2277 2282 list_add(&genpd->gpd_list_node, &gpd_list); ··· 2304 2287 2305 2288 if (genpd->has_provider) { 2306 2289 genpd_unlock(genpd); 2307 - pr_err("Provider present, unable to remove %s\n", genpd->name); 2290 + pr_err("Provider present, unable to remove %s\n", dev_name(&genpd->dev)); 2308 2291 return -EBUSY; 2309 2292 } 2310 2293 2311 2294 if (!list_empty(&genpd->parent_links) || genpd->device_count) { 2312 2295 genpd_unlock(genpd); 2313 - pr_err("%s: unable to remove %s\n", __func__, genpd->name); 2296 + pr_err("%s: unable to remove %s\n", __func__, dev_name(&genpd->dev)); 2314 2297 return -EBUSY; 2315 2298 } 2316 2299 ··· 2324 2307 genpd_unlock(genpd); 2325 2308 genpd_debug_remove(genpd); 2326 2309 cancel_work_sync(&genpd->power_off_work); 2310 + if (genpd->device_id != -ENXIO) 2311 + ida_free(&genpd_ida, genpd->device_id); 2327 2312 genpd_free_data(genpd); 2328 2313 2329 - pr_debug("%s: removed %s\n", __func__, genpd->name); 2314 + pr_debug("%s: removed %s\n", __func__, dev_name(&genpd->dev)); 2330 2315 2331 2316 return 0; 2332 2317 } ··· 3291 3272 else 3292 3273 snprintf(state, sizeof(state), "%s", 3293 3274 status_lookup[genpd->status]); 3294 - seq_printf(s, "%-30s %-30s %u", genpd->name, state, genpd->performance_state); 3275 + seq_printf(s, "%-30s %-30s %u", dev_name(&genpd->dev), state, genpd->performance_state); 3295 3276 3296 3277 /* 3297 3278 * Modifications on the list require holding locks on both 3298 3279 * parent and child, so we are safe. 3299 - * Also genpd->name is immutable. 3280 + * Also the device name is immutable. 3300 3281 */ 3301 3282 list_for_each_entry(link, &genpd->parent_links, parent_node) { 3302 3283 if (list_is_first(&link->parent_node, &genpd->parent_links)) ··· 3521 3502 if (!genpd_debugfs_dir) 3522 3503 return; 3523 3504 3524 - d = debugfs_create_dir(genpd->name, genpd_debugfs_dir); 3505 + d = debugfs_create_dir(dev_name(&genpd->dev), genpd_debugfs_dir); 3525 3506 3526 3507 debugfs_create_file("current_state", 0444, 3527 3508 d, genpd, &status_fops);
+6
include/linux/pm_domain.h
··· 92 92 * GENPD_FLAG_OPP_TABLE_FW: The genpd provider supports performance states, 93 93 * but its corresponding OPP tables are not 94 94 * described in DT, but are given directly by FW. 95 + * 96 + * GENPD_FLAG_DEV_NAME_FW: Instructs genpd to generate an unique device name 97 + * using ida. It is used by genpd providers which 98 + * get their genpd-names directly from FW. 95 99 */ 96 100 #define GENPD_FLAG_PM_CLK (1U << 0) 97 101 #define GENPD_FLAG_IRQ_SAFE (1U << 1) ··· 105 101 #define GENPD_FLAG_RPM_ALWAYS_ON (1U << 5) 106 102 #define GENPD_FLAG_MIN_RESIDENCY (1U << 6) 107 103 #define GENPD_FLAG_OPP_TABLE_FW (1U << 7) 104 + #define GENPD_FLAG_DEV_NAME_FW (1U << 8) 108 105 109 106 enum gpd_status { 110 107 GENPD_STATE_ON = 0, /* PM domain is on */ ··· 168 163 atomic_t sd_count; /* Number of subdomains with power "on" */ 169 164 enum gpd_status status; /* Current state of the domain */ 170 165 unsigned int device_count; /* Number of devices */ 166 + unsigned int device_id; /* unique device id */ 171 167 unsigned int suspended_count; /* System suspend device counter */ 172 168 unsigned int prepared_count; /* Suspend counter of prepared devices */ 173 169 unsigned int performance_state; /* Aggregated max performance state */