EDP: adding debugfs support
Sivaram Nair [Mon, 18 Feb 2013 21:09:52 +0000 (23:09 +0200)]
This patch adds debufs support for the EDP manager and clients. Client
drivers may use the client object's dentry pointer to add their own
debugfs entries.

Change-Id: I7735d48f01db9cf8f7e9c5a08099bbf1f07a7069
Signed-off-by: Sivaram Nair <sivaramn@nvidia.com>
Reviewed-on: http://git-master/r/201843
Reviewed-by: Riham Haidar <rhaidar@nvidia.com>
Tested-by: Riham Haidar <rhaidar@nvidia.com>

arch/arm/mach-tegra/edp.c
drivers/edp/Makefile
drivers/edp/edp.c
drivers/edp/edp_debug.c [new file with mode: 0644]
drivers/edp/edp_internal.h
include/linux/edp.h

index 1e40520..e20825d 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/uaccess.h>
+#include <linux/edp.h>
 #include <mach/edp.h>
 
 #include "fuse.h"
@@ -1052,6 +1053,18 @@ static const struct file_operations edp_reg_override_debugfs_fops = {
        .release        = single_release,
 };
 
+#ifdef CONFIG_EDP_FRAMEWORK
+static __init struct dentry *tegra_edp_debugfs_dir(void)
+{
+       return edp_debugfs_dir;
+}
+#else
+static __init struct dentry *tegra_edp_debugfs_dir(void)
+{
+       return debugfs_create_dir("edp", NULL);
+}
+#endif
+
 static int __init tegra_edp_debugfs_init(void)
 {
        struct dentry *d_edp;
@@ -1060,7 +1073,7 @@ static int __init tegra_edp_debugfs_init(void)
        struct dentry *edp_dir;
        struct dentry *vdd_cpu_dir;
 
-       edp_dir = debugfs_create_dir("edp", NULL);
+       edp_dir = tegra_edp_debugfs_dir();
 
        if (!edp_dir)
                goto edp_dir_err;
index ce20799..7a9b2fd 100644 (file)
@@ -2,6 +2,7 @@ GCOV_PROFILE := y
 
 obj-y                  += edp.o
 obj-y                  += edp_bestfit.o
+obj-$(CONFIG_DEBUG_FS) += edp_debug.o
 obj-y                  += edp_fair.o
 obj-y                  += edp_overage.o
 obj-y                  += edp_priority.o
index e4a728f..311e838 100644 (file)
@@ -50,6 +50,12 @@ static void promote(struct work_struct *work)
        mutex_unlock(&edp_lock);
 }
 
+void schedule_promotion(struct edp_manager *mgr)
+{
+       if (mgr->remaining && mgr->num_denied && mgr->gov->promote)
+               schedule_work(&mgr->work);
+}
+
 int edp_register_manager(struct edp_manager *mgr)
 {
        int r = -EEXIST;
@@ -70,6 +76,7 @@ int edp_register_manager(struct edp_manager *mgr)
                INIT_WORK(&mgr->work, promote);
                mgr->kobj = NULL;
                edp_manager_add_kobject(mgr);
+               manager_add_dentry(mgr);
                r = 0;
        }
        mutex_unlock(&edp_lock);
@@ -128,6 +135,7 @@ int edp_unregister_manager(struct edp_manager *mgr)
        } else if (!list_empty(&mgr->clients)) {
                r = -EBUSY;
        } else {
+               manager_remove_dentry(mgr);
                edp_manager_remove_kobject(mgr);
                edp_set_governor_unlocked(mgr, NULL);
                list_del(&mgr->link);
@@ -166,7 +174,7 @@ static struct edp_client *find_client(struct edp_manager *mgr,
        return NULL;
 }
 
-static unsigned int e0_current_sum(struct edp_manager *mgr)
+unsigned int e0_current_sum(struct edp_manager *mgr)
 {
        struct edp_client *p;
        unsigned int sum = 0;
@@ -238,6 +246,7 @@ static int register_client(struct edp_manager *mgr, struct edp_client *client)
        client->ithreshold = client->states[0];
        client->kobj = NULL;
        edp_client_add_kobject(client);
+       client_add_dentry(client);
 
        return 0;
 }
@@ -352,8 +361,8 @@ static int mod_request(struct edp_client *client, const unsigned int *req)
        update_loans(client);
 
        /* Do not block calling clients for promotions */
-       if (m->remaining > prev_remain && m->num_denied && m->gov->promote)
-               schedule_work(&m->work);
+       if (m->remaining > prev_remain)
+               schedule_promotion(m);
 
        return 0;
 }
@@ -391,6 +400,7 @@ static int unregister_client(struct edp_client *client)
        if (client->num_loans)
                return -EBUSY;
 
+       client_remove_dentry(client);
        edp_client_remove_kobject(client);
        close_all_loans(client);
        mod_request(client, NULL);
diff --git a/drivers/edp/edp_debug.c b/drivers/edp/edp_debug.c
new file mode 100644 (file)
index 0000000..cdb3936
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/edp.h>
+#include <linux/debugfs.h>
+#include "edp_internal.h"
+
+struct dentry *edp_debugfs_dir;
+
+static int __manager_cap_set(struct edp_manager *m, unsigned int new_max)
+{
+       if (new_max < e0_current_sum(m))
+               return -EINVAL;
+
+       if (new_max < m->max - m->remaining)
+               return -EBUSY;
+
+       if (new_max > m->max) {
+               m->remaining += new_max - m->max;
+               m->max = new_max;
+               schedule_promotion(m);
+       } else {
+               m->remaining -= m->max - new_max;
+               m->max = new_max;
+       }
+
+       return 0;
+}
+
+static int manager_status_show(struct seq_file *file, void *data)
+{
+       struct edp_manager *m;
+       struct edp_client *c;
+
+       if (!file->private)
+               return -ENODEV;
+
+       m = file->private;
+
+       mutex_lock(&edp_lock);
+
+       seq_printf(file, "cap      : %u\n", m->max);
+       seq_printf(file, "remaining: %u\n", m->remaining);
+
+       seq_printf(file, "------------------------------------------\n");
+       seq_printf(file, "%-16s %3s %5s %7s %7s\n",
+                       "client", "pri", "E0", "request", "current");
+       seq_printf(file, "------------------------------------------\n");
+
+       list_for_each_entry(c, &m->clients, link)
+               seq_printf(file, "%-16s %3d %5u %7u %7u\n", c->name,
+                               c->priority, e0_level(c), req_level(c),
+                               cur_level(c));
+
+       mutex_unlock(&edp_lock);
+       return 0;
+}
+
+static int manager_status_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, manager_status_show, inode->i_private);
+}
+
+static const struct file_operations manager_status_fops = {
+       .open = manager_status_open,
+       .read = seq_read,
+};
+
+static int manager_cap_set(void *data, u64 val)
+{
+       struct edp_manager *m = data;
+       int r;
+
+       mutex_lock(&edp_lock);
+       r = __manager_cap_set(m, val);
+       mutex_unlock(&edp_lock);
+       return r;
+}
+
+static int manager_cap_get(void *data, u64 *val)
+{
+       struct edp_manager *m = data;
+
+       mutex_lock(&edp_lock);
+       *val = m->max;
+       mutex_unlock(&edp_lock);
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(manager_cap_fops, manager_cap_get,
+               manager_cap_set, "%lld\n");
+
+void manager_add_dentry(struct edp_manager *m)
+{
+       struct dentry *d;
+
+       if (!edp_debugfs_dir)
+               return;
+
+       d = debugfs_create_dir(m->name, edp_debugfs_dir);
+       if (IS_ERR_OR_NULL(d))
+               return;
+
+       m->dentry = d;
+
+       d = debugfs_create_file("status", S_IRUGO, m->dentry, m,
+                       &manager_status_fops);
+       WARN_ON(IS_ERR_OR_NULL(d));
+
+       d = debugfs_create_file("cap", S_IRUGO | S_IWUSR, m->dentry, m,
+                       &manager_cap_fops);
+       WARN_ON(IS_ERR_OR_NULL(d));
+}
+
+void manager_remove_dentry(struct edp_manager *m)
+{
+       debugfs_remove_recursive(m->dentry);
+       m->dentry = NULL;
+}
+
+static int __client_current_set(struct edp_client *c, unsigned int new)
+{
+       struct edp_manager *m;
+       unsigned int nl;
+       unsigned int cl;
+
+       if (new >= c->num_states)
+               return -EINVAL;
+
+       nl = c->states[new];
+       cl = cur_level(c);
+       m = c->manager;
+
+       if (nl > cl && nl - cl > m->remaining)
+               return -EBUSY;
+
+       c->cur = c->states + new;
+       c->req = c->states + new;
+
+       if (nl < cl) {
+               m->remaining += cl - nl;
+               if (c->throttle)
+                       c->throttle(new, c->private_data);
+       } else if (nl > cl) {
+               m->remaining -= nl - cl;
+               if (c->notify_promotion)
+                       c->notify_promotion(new, c->private_data);
+       }
+
+       return 0;
+}
+
+static int client_current_set(void *data, u64 val)
+{
+       struct edp_client *c = data;
+       int r;
+
+       mutex_lock(&edp_lock);
+       r = __client_current_set(c, val);
+       mutex_unlock(&edp_lock);
+       return r;
+}
+
+static int client_current_get(void *data, u64 *val)
+{
+       struct edp_client *c = data;
+
+       mutex_lock(&edp_lock);
+       *val = cur_level(c);
+       mutex_unlock(&edp_lock);
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(client_current_fops, client_current_get,
+               client_current_set, "%lld\n");
+
+void client_add_dentry(struct edp_client *c)
+{
+       struct dentry *d;
+
+       if (!c->manager->dentry)
+               return;
+
+       d = debugfs_create_dir(c->name, c->manager->dentry);
+       if (IS_ERR_OR_NULL(d)) {
+               WARN_ON(1);
+               return;
+       }
+
+       c->dentry = d;
+
+       d = debugfs_create_file("current", S_IRUGO | S_IWUSR, c->dentry,
+                       c, &client_current_fops);
+       WARN_ON(IS_ERR_OR_NULL(d));
+}
+
+void client_remove_dentry(struct edp_client *c)
+{
+       debugfs_remove_recursive(c->dentry);
+       c->dentry = NULL;
+}
+
+static void dbg_update_request(struct edp_client *c, const unsigned int *r) {}
+static void dbg_update_loans(struct edp_client *c) {}
+static void dbg_promote(struct edp_manager *mgr) {}
+
+static struct edp_governor dbg_governor = {
+       .name = "debug",
+       .owner = THIS_MODULE,
+       .update_request = dbg_update_request,
+       .update_loans = dbg_update_loans,
+       .promote = dbg_promote
+};
+
+static int __init debug_init(void)
+{
+       struct dentry *d;
+
+       d = debugfs_create_dir("edp", NULL);
+       if (IS_ERR_OR_NULL(d)) {
+               WARN_ON(1);
+               return -EFAULT;
+       }
+
+       edp_debugfs_dir = d;
+       return edp_register_governor(&dbg_governor);
+}
+postcore_initcall(debug_init);
index 51b11ea..f11f8c2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2012-2013, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -73,4 +73,11 @@ void edp_default_update_loans(struct edp_client *lender);
 unsigned int edp_throttling_point(struct edp_client *c, unsigned int deficit);
 unsigned int edp_promotion_point(struct edp_client *c, unsigned int step);
 
+void manager_add_dentry(struct edp_manager *m);
+void manager_remove_dentry(struct edp_manager *m);
+void client_add_dentry(struct edp_client *c);
+void client_remove_dentry(struct edp_client *c);
+void schedule_promotion(struct edp_manager *m);
+unsigned int e0_current_sum(struct edp_manager *mgr);
+
 #endif
index 7a72072..1fba4bc 100644 (file)
@@ -41,6 +41,11 @@ struct edp_manager {
 
        /* governor internal */
        void *gov_data;
+
+#ifdef CONFIG_DEBUG_FS
+       /* public */
+       struct dentry *dentry;
+#endif
 };
 
 /*
@@ -85,6 +90,11 @@ struct edp_client {
        /* governor internal */
        unsigned int gwt;
        struct list_head glnk;
+
+#ifdef CONFIG_DEBUG_FS
+       /* public */
+       struct dentry *dentry;
+#endif
 };
 
 struct edp_governor {
@@ -104,6 +114,8 @@ struct edp_governor {
 };
 
 #ifdef CONFIG_EDP_FRAMEWORK
+extern struct dentry *edp_debugfs_dir;
+
 extern int edp_register_manager(struct edp_manager *mgr);
 extern int edp_unregister_manager(struct edp_manager *mgr);
 extern struct edp_manager *edp_get_manager(const char *name);