Re: [PATCH] PM QoS: Add debugfs support to view the list of constraints

From: Rafael J. Wysocki
Date: Fri Dec 05 2014 - 17:25:58 EST


On Friday, December 05, 2014 11:19:08 AM Dave Gerlach wrote:
> From: Nishanth Menon <nm@xxxxxx>
>
> PM QoS requests are notoriously hard to debug and made even
> more so due to their highly dynamic nature. Having visibility
> into the internal data representation per constraint allows
> us to have much better appreciation of potential issues or
> bad usage by drivers in the system.
>
> So introduce for all classes of PM QoS, an entry in
> /sys/kernel/debug/pm_qos that shall show all the current
> requests as well as the snapshot of the value these requests
> boil down to. For example:
> ==> /sys/kernel/debug/pm_qos/cpu_dma_latency <==
> 1: 4444: Active
> 2: 2000000000: Default
> 3: 2000000000: Default
> 4: 2000000000: Default
> Type=Minimum, Value=4444, Requests: active=1 / total=4
>
> ==> /sys/kernel/debug/pm_qos/memory_bandwidth <==
> Empty!
>
> ...
>
> The actual value listed will have their meaning based
> on the QoS it is on, the 'Type' indicates what logic
> it would use to collate the information - Minimum,
> Maximum, or Sum. Value is the collation of all requests.
> This interface also compares the values with the defaults
> for the QoS class and marks the ones that are
> currently active.
>
> Signed-off-by: Nishanth Menon <nm@xxxxxx>
> Signed-off-by: Dave Gerlach <d-gerlach@xxxxxx>

This is fine by me, but let's wait and see if there are any comments.

> ---
> kernel/power/qos.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 89 insertions(+), 2 deletions(-)
>
> diff --git a/kernel/power/qos.c b/kernel/power/qos.c
> index 5f4c006..97b0df7 100644
> --- a/kernel/power/qos.c
> +++ b/kernel/power/qos.c
> @@ -41,6 +41,8 @@
> #include <linux/platform_device.h>
> #include <linux/init.h>
> #include <linux/kernel.h>
> +#include <linux/debugfs.h>
> +#include <linux/seq_file.h>
>
> #include <linux/uaccess.h>
> #include <linux/export.h>
> @@ -182,6 +184,81 @@ static inline void pm_qos_set_value(struct pm_qos_constraints *c, s32 value)
> c->target_value = value;
> }
>
> +static inline int pm_qos_get_value(struct pm_qos_constraints *c);
> +static int pm_qos_dbg_show_requests(struct seq_file *s, void *unused)
> +{
> + struct pm_qos_object *qos = (struct pm_qos_object *)s->private;
> + struct pm_qos_constraints *c;
> + struct pm_qos_request *req;
> + char *type;
> + unsigned long flags;
> + int tot_reqs = 0;
> + int active_reqs = 0;
> +
> + if (IS_ERR_OR_NULL(qos)) {
> + pr_err("%s: bad qos param!\n", __func__);
> + return -EINVAL;
> + }
> + c = qos->constraints;
> + if (IS_ERR_OR_NULL(c)) {
> + pr_err("%s: Bad constraints on qos?\n", __func__);
> + return -EINVAL;
> + }
> +
> + /* Lock to ensure we have a snapshot */
> + spin_lock_irqsave(&pm_qos_lock, flags);
> + if (plist_head_empty(&c->list)) {
> + seq_puts(s, "Empty!\n");
> + goto out;
> + }
> +
> + switch (c->type) {
> + case PM_QOS_MIN:
> + type = "Minimum";
> + break;
> + case PM_QOS_MAX:
> + type = "Maximum";
> + break;
> + case PM_QOS_SUM:
> + type = "Sum";
> + break;
> + default:
> + type = "Unknown";
> + }
> +
> + plist_for_each_entry(req, &c->list, node) {
> + char *state = "Default";
> +
> + if ((req->node).prio != c->default_value) {
> + active_reqs++;
> + state = "Active";
> + }
> + tot_reqs++;
> + seq_printf(s, "%d: %d: %s\n", tot_reqs,
> + (req->node).prio, state);
> + }
> +
> + seq_printf(s, "Type=%s, Value=%d, Requests: active=%d / total=%d\n",
> + type, pm_qos_get_value(c), active_reqs, tot_reqs);
> +
> +out:
> + spin_unlock_irqrestore(&pm_qos_lock, flags);
> + return 0;
> +}
> +
> +static int pm_qos_dbg_open(struct inode *inode, struct file *file)
> +{
> + return single_open(file, pm_qos_dbg_show_requests,
> + inode->i_private);
> +}
> +
> +static const struct file_operations pm_qos_debug_fops = {
> + .open = pm_qos_dbg_open,
> + .read = seq_read,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> /**
> * pm_qos_update_target - manages the constraints list and calls the notifiers
> * if needed
> @@ -509,12 +586,17 @@ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
> EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
>
> /* User space interface to PM QoS classes via misc devices */
> -static int register_pm_qos_misc(struct pm_qos_object *qos)
> +static int register_pm_qos_misc(struct pm_qos_object *qos, struct dentry *d)
> {
> qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
> qos->pm_qos_power_miscdev.name = qos->name;
> qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
>
> + if (d) {
> + (void)debugfs_create_file(qos->name, S_IRUGO, d,
> + (void *)qos, &pm_qos_debug_fops);
> + }
> +
> return misc_register(&qos->pm_qos_power_miscdev);
> }
>
> @@ -608,11 +690,16 @@ static int __init pm_qos_power_init(void)
> {
> int ret = 0;
> int i;
> + struct dentry *d;
>
> BUILD_BUG_ON(ARRAY_SIZE(pm_qos_array) != PM_QOS_NUM_CLASSES);
>
> + d = debugfs_create_dir("pm_qos", NULL);
> + if (IS_ERR_OR_NULL(d))
> + d = NULL;
> +
> for (i = PM_QOS_CPU_DMA_LATENCY; i < PM_QOS_NUM_CLASSES; i++) {
> - ret = register_pm_qos_misc(pm_qos_array[i]);
> + ret = register_pm_qos_misc(pm_qos_array[i], d);
> if (ret < 0) {
> printk(KERN_ERR "pm_qos_param: %s setup failed\n",
> pm_qos_array[i]->name);
>

--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/