From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753759AbaIOLK3 (ORCPT ); Mon, 15 Sep 2014 07:10:29 -0400 Received: from mailout3.w1.samsung.com ([210.118.77.13]:22243 "EHLO mailout3.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753289AbaIOLK1 (ORCPT ); Mon, 15 Sep 2014 07:10:27 -0400 X-AuditID: cbfec7f4-b7f156d0000063c7-26-5416c921a52f From: Maciej Matraszek To: linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, "Rafael J. Wysocki" , Len Brown , Pavel Machek , Greg Kroah-Hartman Cc: Krzysztof Kozlowski , Bartlomiej Zolnierkiewicz , Geert Uytterhoeven , Al Viro , Maciej Matraszek , Maciej Matraszek Subject: [PATCH v2] PM / Domains: add debugfs listing of struct generic_pm_domain-s Date: Mon, 15 Sep 2014 13:09:10 +0200 Message-id: <1410779350-20545-1-git-send-email-m.matraszek@samsung.com> X-Mailer: git-send-email 1.9.1 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFuphluLIzCtJLcpLzFFi42I5/e/4ZV3Fk2IhBguv8VtsnLGe1eLZrb1M Fs2L17NZvH5haDFrCpB3edccNovPvUcYLU6+uMxo0T/lCpvF3VNH2SzOnL7EanH+73FWBx6P nbPusnss3vOSyePQ4Q5Gj/1z17B7bLnazuLRt2UVo8eK1d/ZPT5vkvPY9OQtUwBnFJdNSmpO Zllqkb5dAlfG6nevGQv+WlS8mnaetYFxilYXIyeHhICJxJ2uJcwQtpjEhXvr2boYuTiEBJYy Spxq+8IO4fQxSVzcvAqsik3AQGLzv34WkISIwDVGiT2LljGDOMwCc5kk+p98YQGpEhYIlfh+ Zw8biM0ioCqxY9oTsG5eAXeJ6SeXsELsk5M4eWwy6wRG7gWMDKsYRVNLkwuKk9JzDfWKE3OL S/PS9ZLzczcxQkLwyw7GxcesDjEKcDAq8fAu3CEWIsSaWFZcmXuIUYKDWUmEt3Y/UIg3JbGy KrUoP76oNCe1+BAjEwenVAOjiGwT+6yjKR12lmfeb+27MM/v4/Lr8ww2aWUnq72/XJyXedg1 ONBTsP3V6ZNMTo9f1Ioy7jNLtmJ0OVrs1dp2uOX92vTM7lvM0nf/fmOeHznhv7voOoZj53gM rZ4x8Z/Vtm6NWvzvy1VRDYNl2rffpWqJBv+7YCV/YjXzFZstCzr5xZhP1OkpsRRnJBpqMRcV JwIAi/GTtB8CAAA= Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add /sys/kernel/debug/pm_genpd/pm_genpd_summary file, which lists power domains in the system, their statuses and attached devices, resembling /sys/kernel/debug/clk/clk_summary. Currently it is impossible to inspect (from userland) whether a power domain is on or off. And, if it is on, which device blocks it from powering down. This change allows developers working on embedded devices power efficiency to list all necessary information about generic power domains in one place. The content of pm_genpd/pm_genpd_summary file is generated by iterating over all generic power domain in the system, and, for each, over registered devices and over the subdomains, if present. Example output: $ cat /sys/kernel/debug/pm_genpd/pm_genpd_summary domain status slaves /device runtime status ---------------------------------------------------------------------- a4su off a3sg off a3sm on a3sp on /devices/e6600000.pwm suspended /devices/e6c50000.serial active /devices/e6850000.sd suspended /devices/e6bd0000.mmc active a4s on a3sp, a3sm, a3sg /devices/e6900000.irqpin unsupported /devices/e6900004.irqpin unsupported /devices/e6900008.irqpin unsupported /devices/e690000c.irqpin unsupported /devices/e9a00000.ethernet active a3rv off a4r off a3rv /devices/fff20000.i2c suspended a4lc off c5 on a4lc, a4r, a4s, a4su /devices/e6050000.pfc unsupported /devices/e6138000.timer active To enable this feature, compile the kernel with debugfs and CONFIG_PM_ADVANCED_DEBUG enabled. Signed-off-by: Maciej Matraszek Tested-by: Geert Uytterhoeven --- v2: - change example output to Geert's one - do not pass unused inode->i_private to single_open() (suggested by Al) - skip checking for no debugfs (suggested by Greg) - remove debugfs files at shutdown (suggested by Greg) drivers/base/power/domain.c | 157 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index eee55c1e5fde..c7424fb8cf0a 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2189,3 +2189,160 @@ void pm_genpd_init(struct generic_pm_domain *genpd, list_add(&genpd->gpd_list_node, &gpd_list); mutex_unlock(&gpd_list_lock); } + + +/*** debugfs support ***/ + +#ifdef CONFIG_PM_ADVANCED_DEBUG +#include +#include +#include +#include +#include +#include +static struct dentry *pm_genpd_debugfs_dir; + +/* + * TODO: This function is a slightly modified version of rtpm_status_show + * from sysfs.c, but dependencies between PM_GENERIC_DOMAINS and PM_RUNTIME + * are too loose to generalize it. + */ +#ifdef CONFIG_PM_RUNTIME +static void rtpm_status_str(struct seq_file *s, struct device *dev) +{ + static const char * const status_lookup[] = { + [RPM_ACTIVE] = "active", + [RPM_RESUMING] = "resuming", + [RPM_SUSPENDED] = "suspended", + [RPM_SUSPENDING] = "suspending" + }; + const char *p = ""; + + if (dev->power.runtime_error) + p = "error"; + else if (dev->power.disable_depth) + p = "unsupported"; + else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup)) + p = status_lookup[dev->power.runtime_status]; + else + WARN_ON(1); + + seq_puts(s, p); +} +#else +static void rtpm_status_str(struct seq_file *s, struct device *dev) +{ + seq_puts(s, "active"); +} +#endif + +static int pm_genpd_summary_one(struct seq_file *s, + struct generic_pm_domain *gpd) +{ + static const char * const status_lookup[] = { + [GPD_STATE_ACTIVE] = "on", + [GPD_STATE_WAIT_MASTER] = "wait-master", + [GPD_STATE_BUSY] = "busy", + [GPD_STATE_REPEAT] = "off-in-progress", + [GPD_STATE_POWER_OFF] = "off" + }; + struct pm_domain_data *pm_data; + const char *kobj_path; + struct gpd_link *link; + int ret; + + ret = mutex_lock_interruptible(&gpd->lock); + if (ret) + return -ERESTARTSYS; + + if (WARN_ON(gpd->status >= ARRAY_SIZE(status_lookup))) + goto exit; + seq_printf(s, "%-30s %-15s ", gpd->name, status_lookup[gpd->status]); + + /* + * Modifications on the list require holding locks on both + * master and slave, so we are safe. + * Also gpd->name is immutable. + */ + list_for_each_entry(link, &gpd->master_links, master_node) { + seq_printf(s, "%s", link->slave->name); + if (!list_is_last(&link->master_node, &gpd->master_links)) + seq_puts(s, ", "); + } + + list_for_each_entry(pm_data, &gpd->dev_list, list_node) { + kobj_path = kobject_get_path(&pm_data->dev->kobj, GFP_KERNEL); + if (kobj_path == NULL) + continue; + + seq_printf(s, "\n %-50s ", kobj_path); + rtpm_status_str(s, pm_data->dev); + kfree(kobj_path); + } + + seq_puts(s, "\n"); +exit: + mutex_unlock(&gpd->lock); + + return 0; +} + +static int pm_genpd_summary_show(struct seq_file *s, void *data) +{ + struct generic_pm_domain *gpd; + int ret = 0; + + seq_puts(s, " domain status slaves\n"); + seq_puts(s, " /device runtime status\n"); + seq_puts(s, "----------------------------------------------------------------------\n"); + + ret = mutex_lock_interruptible(&gpd_list_lock); + if (ret) + return -ERESTARTSYS; + + list_for_each_entry(gpd, &gpd_list, gpd_list_node) { + ret = pm_genpd_summary_one(s, gpd); + if (ret) + break; + } + mutex_unlock(&gpd_list_lock); + + return ret; +} + +static int pm_genpd_summary_open(struct inode *inode, struct file *file) +{ + return single_open(file, pm_genpd_summary_show, NULL); +} + +static const struct file_operations pm_genpd_summary_fops = { + .open = pm_genpd_summary_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init pm_genpd_debug_init(void) +{ + struct dentry *d; + + pm_genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL); + + if (!pm_genpd_debugfs_dir) + return -ENOMEM; + + d = debugfs_create_file("pm_genpd_summary", S_IRUGO, + pm_genpd_debugfs_dir, NULL, &pm_genpd_summary_fops); + if (!d) + return -ENOMEM; + + return 0; +} +late_initcall(pm_genpd_debug_init); + +static void __exit pm_genpd_debug_exit(void) +{ + debugfs_remove_recursive(pm_genpd_debugfs_dir); +} +__exitcall(pm_genpd_debug_exit); +#endif /* CONFIG_PM_ADVANCED_DEBUG */ -- 1.9.1