pm: EDP: adding sysfs entries
Sivaram Nair [Tue, 28 Aug 2012 16:08:29 +0000 (19:08 +0300)]
This patch adds sysfs entries to represent the EDP framework.  The sysfs
is at /sys/power/edp.

Change-Id: If562d7745a99e5324a982f91991d2dfeacacb84e
Signed-off-by: Sivaram Nair <sivaramn@nvidia.com>
Reviewed-on: http://git-master/r/130510
Reviewed-by: Juha Tukkinen <jtukkinen@nvidia.com>
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>

Rebase-Id: Rb266b16ac6e6529a3f4c06c4ad791663ee25d483

drivers/edp/Makefile
drivers/edp/edp.c
drivers/edp/edp_internal.h
drivers/edp/sysfs.c [new file with mode: 0644]
include/linux/edp.h

index 134381f..2f8d84c 100644 (file)
@@ -1,2 +1,3 @@
 obj-$(CONFIG_EDP_FRAMEWORK)    += edp.o
 obj-$(CONFIG_EDP_FRAMEWORK)    += edp_priority.o
+obj-$(CONFIG_EDP_FRAMEWORK)    += sysfs.o
index f08e3ca..13b77a2 100644 (file)
@@ -64,6 +64,8 @@ int edp_register_manager(struct edp_manager *mgr)
                mgr->gov = NULL;
                INIT_LIST_HEAD(&mgr->clients);
                INIT_WORK(&mgr->work, promote);
+               mgr->kobj = NULL;
+               edp_manager_add_kobject(mgr);
                r = 0;
        }
        mutex_unlock(&edp_lock);
@@ -122,6 +124,7 @@ int edp_unregister_manager(struct edp_manager *mgr)
        } else if (!list_empty(&mgr->clients)) {
                r = -EBUSY;
        } else {
+               edp_manager_remove_kobject(mgr);
                edp_set_governor_unlocked(mgr, NULL);
                list_del(&mgr->link);
                mgr->registered = false;
@@ -228,6 +231,8 @@ static int register_client(struct edp_manager *mgr, struct edp_client *client)
        client->num_borrowers = 0;
        client->num_loans = 0;
        client->ithreshold = client->states[0];
+       client->kobj = NULL;
+       edp_client_add_kobject(client);
 
        return 0;
 }
@@ -305,6 +310,7 @@ static int unregister_client(struct edp_client *client)
        if (client->num_loans)
                return -EBUSY;
 
+       edp_client_remove_kobject(client);
        close_all_loans(client);
        mod_request(client, NULL);
        list_del(&client->link);
index 227e42c..e0d8902 100644 (file)
@@ -58,4 +58,9 @@ struct edp_governor *edp_find_governor_unlocked(const char *s);
 int edp_set_governor_unlocked(struct edp_manager *mgr,
                struct edp_governor *gov);
 
+void edp_manager_add_kobject(struct edp_manager *mgr);
+void edp_manager_remove_kobject(struct edp_manager *mgr);
+void edp_client_add_kobject(struct edp_client *client);
+void edp_client_remove_kobject(struct edp_client *client);
+
 #endif
diff --git a/drivers/edp/sysfs.c b/drivers/edp/sysfs.c
new file mode 100644 (file)
index 0000000..083aa17
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * Copyright (c) 2012, 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/slab.h>
+#include "edp_internal.h"
+
+static struct kobject edp_kobj;
+
+struct manager_entry {
+       struct edp_manager *manager;
+       struct kobject kobj;
+};
+
+struct manager_attr {
+       struct attribute attr;
+       ssize_t (*show)(struct edp_manager *, char *);
+       ssize_t (*store)(struct edp_manager *, const char *, size_t);
+};
+
+static ssize_t cap_show(struct edp_manager *m, char *s)
+{
+       return scnprintf(s, PAGE_SIZE, "%u\n", m->imax);
+}
+
+static ssize_t remaining_show(struct edp_manager *m, char *s)
+{
+       return scnprintf(s, PAGE_SIZE, "%u\n", m->remaining);
+}
+
+static ssize_t manager_governor_show(struct edp_manager *m, char *s)
+{
+       return scnprintf(s, PAGE_SIZE, "%s\n", m->gov ? m->gov->name : "");
+}
+
+static ssize_t manager_governor_store(struct edp_manager *m, const char *s,
+               size_t count)
+{
+       char name[EDP_NAME_LEN];
+       struct edp_governor *gov;
+
+       if (!count || count >= sizeof(name))
+               return -EINVAL;
+
+       memcpy(name, s, count);
+       name[count] = 0;
+       strim(name);
+       gov = edp_find_governor_unlocked(name);
+       if (!gov)
+               return -EINVAL;
+
+       return edp_set_governor_unlocked(m, gov) ?: count;
+}
+
+struct manager_attr attr_cap = __ATTR_RO(cap);
+struct manager_attr attr_remaining = __ATTR_RO(remaining);
+struct manager_attr attr_mgr_gov = __ATTR(governor, 0644,
+               manager_governor_show, manager_governor_store);
+
+static struct attribute *manager_attrs[] = {
+       &attr_cap.attr,
+       &attr_remaining.attr,
+       &attr_mgr_gov.attr,
+       NULL
+};
+
+static struct edp_manager *to_manager(struct kobject *kobj)
+{
+       struct manager_entry *me = container_of(kobj, struct manager_entry,
+                       kobj);
+       return me ? me->manager : NULL;
+}
+
+static ssize_t manager_state_show(struct kobject *kobj,
+               struct attribute *attr, char *buf)
+{
+       ssize_t r;
+       struct edp_manager *m;
+       struct manager_attr *mattr;
+
+       mutex_lock(&edp_lock);
+       m = to_manager(kobj);
+       mattr = container_of(attr, struct manager_attr, attr);
+       r = m && mattr ? mattr->show(m, buf) : -EINVAL;
+       mutex_unlock(&edp_lock);
+
+       return r;
+}
+
+static ssize_t manager_state_store(struct kobject *kobj,
+               struct attribute *attr, const char *buf, size_t count)
+{
+       ssize_t r;
+       struct edp_manager *m;
+       struct manager_attr *mattr;
+
+       mutex_lock(&edp_lock);
+       m = to_manager(kobj);
+       mattr = container_of(attr, struct manager_attr, attr);
+       r = m && mattr ? mattr->store(m, buf, count) : -EINVAL;
+       mutex_unlock(&edp_lock);
+
+       return r;
+}
+
+static const struct sysfs_ops manager_sysfs_ops = {
+       .show = manager_state_show,
+       .store = manager_state_store
+};
+
+static struct kobj_type ktype_manager = {
+       .sysfs_ops = &manager_sysfs_ops,
+       .default_attrs = manager_attrs
+};
+
+void edp_manager_add_kobject(struct edp_manager *mgr)
+{
+       struct manager_entry *me;
+
+       me = kzalloc(sizeof(*me), GFP_KERNEL);
+       if (!me) {
+               pr_err("%s: failed to alloc sysfs manager entry\n",
+                               mgr->name);
+               return;
+       }
+
+       if (kobject_init_and_add(&me->kobj, &ktype_manager, &edp_kobj,
+                               mgr->name)) {
+               pr_err("%s: failed to init & add sysfs manager entry\n",
+                               mgr->name);
+               kfree(me);
+               return;
+       }
+
+       me->manager = mgr;
+       mgr->kobj = &me->kobj;
+       kobject_uevent(&me->kobj, KOBJ_ADD);
+       return;
+}
+
+void edp_manager_remove_kobject(struct edp_manager *mgr)
+{
+       struct manager_entry *me;
+
+       if (!mgr->kobj)
+               return;
+
+       me = container_of(mgr->kobj, struct manager_entry, kobj);
+       mgr->kobj = NULL;
+       kobject_put(&me->kobj);
+       kfree(me);
+}
+
+struct client_entry {
+       struct edp_client *client;
+       struct kobject kobj;
+};
+
+struct client_attr {
+       struct attribute attr;
+       ssize_t (*show)(struct edp_client *, char *);
+};
+
+static ssize_t states_show(struct edp_client *c, char *s)
+{
+       unsigned int i;
+       int cnt = 0;
+       const int sz = sizeof(*c->states) * 3 + 2;
+
+       for (i = 0; i < c->num_states && (cnt + sz) < PAGE_SIZE; i++)
+               cnt += sprintf(s + cnt, "%s%u", i ? " " : "", c->states[i]);
+
+       cnt += sprintf(s + cnt, "\n");
+       return cnt;
+}
+
+static ssize_t num_states_show(struct edp_client *c, char *s)
+{
+       return scnprintf(s, PAGE_SIZE, "%u\n", c->num_states);
+}
+
+static ssize_t e0_show(struct edp_client *c, char *s)
+{
+       return scnprintf(s, PAGE_SIZE, "%u\n", c->states[c->e0_index]);
+}
+
+static ssize_t max_borrowers_show(struct edp_client *c, char *s)
+{
+       return scnprintf(s, PAGE_SIZE, "%u\n", c->max_borrowers);
+}
+
+static ssize_t priority_show(struct edp_client *c, char *s)
+{
+       return scnprintf(s, PAGE_SIZE, "%d\n", c->priority);
+}
+
+static ssize_t request_show(struct edp_client *c, char *s)
+{
+       return scnprintf(s, PAGE_SIZE, "%u\n", req_level(c));
+}
+
+static ssize_t current_show(struct edp_client *c, char *s)
+{
+       return scnprintf(s, PAGE_SIZE, "%u\n", cur_level(c));
+}
+
+static ssize_t threshold_show(struct edp_client *c, char *s)
+{
+       return scnprintf(s, PAGE_SIZE, "%u\n", c->ithreshold);
+}
+
+static ssize_t borrowers_show(struct edp_client *c, char *s)
+{
+       return scnprintf(s, PAGE_SIZE, "%u\n", c->num_borrowers);
+}
+
+static ssize_t loans_show(struct edp_client *c, char *s)
+{
+       return scnprintf(s, PAGE_SIZE, "%u\n", c->num_loans);
+}
+
+struct client_attr attr_states = __ATTR_RO(states);
+struct client_attr attr_num_states = __ATTR_RO(num_states);
+struct client_attr attr_e0 = __ATTR_RO(e0);
+struct client_attr attr_max_borrowers = __ATTR_RO(max_borrowers);
+struct client_attr attr_priority = __ATTR_RO(priority);
+struct client_attr attr_request = __ATTR_RO(request);
+struct client_attr attr_current = __ATTR_RO(current);
+struct client_attr attr_threshold = __ATTR_RO(threshold);
+struct client_attr attr_borrowers = __ATTR_RO(borrowers);
+struct client_attr attr_loans = __ATTR_RO(loans);
+
+static struct attribute *client_attrs[] = {
+       &attr_states.attr,
+       &attr_num_states.attr,
+       &attr_e0.attr,
+       &attr_max_borrowers.attr,
+       &attr_priority.attr,
+       &attr_request.attr,
+       &attr_current.attr,
+       &attr_threshold.attr,
+       &attr_borrowers.attr,
+       &attr_loans.attr,
+       NULL
+};
+
+static struct edp_client *to_client(struct kobject *kobj)
+{
+       struct client_entry *ce = container_of(kobj, struct client_entry,
+                       kobj);
+       return ce ? ce->client : NULL;
+}
+
+static ssize_t client_state_show(struct kobject *kobj,
+               struct attribute *attr, char *buf)
+{
+       ssize_t r;
+       struct edp_client *c;
+       struct client_attr *cattr;
+
+       mutex_lock(&edp_lock);
+       c = to_client(kobj);
+       cattr = container_of(attr, struct client_attr, attr);
+       r = c && cattr ? cattr->show(c, buf) : -EINVAL;
+       mutex_unlock(&edp_lock);
+
+       return r;
+}
+
+static const struct sysfs_ops client_sysfs_ops = {
+       .show = client_state_show
+};
+
+static struct kobj_type ktype_client = {
+       .sysfs_ops = &client_sysfs_ops,
+       .default_attrs = client_attrs
+};
+
+void edp_client_add_kobject(struct edp_client *client)
+{
+       struct client_entry *ce;
+       struct kobject *parent = client->manager->kobj;
+
+       if (!parent)
+               return;
+
+       ce = kzalloc(sizeof(*ce), GFP_KERNEL);
+       if (!ce) {
+               pr_err("%s: failed to alloc sysfs client entry\n",
+                               client->name);
+               return;
+       }
+
+       if (kobject_init_and_add(&ce->kobj, &ktype_client, parent,
+                               client->name)) {
+               pr_err("%s: failed to init & add sysfs client entry\n",
+                               client->name);
+               kfree(ce);
+               return;
+       }
+
+       ce->client = client;
+       client->kobj = &ce->kobj;
+       kobject_uevent(&ce->kobj, KOBJ_ADD);
+       return;
+}
+
+void edp_client_remove_kobject(struct edp_client *client)
+{
+       struct client_entry *ce;
+
+       if (!client->kobj)
+               return;
+
+       ce = container_of(client->kobj, struct client_entry, kobj);
+       client->kobj = NULL;
+       kobject_put(&ce->kobj);
+       kfree(ce);
+}
+
+static ssize_t governors_show(struct kobject *kobj, struct attribute *attr,
+               char *s)
+{
+       struct edp_governor *g;
+       int cnt = 0;
+
+       mutex_lock(&edp_lock);
+
+       list_for_each_entry(g, &edp_governors, link) {
+               if (cnt + EDP_NAME_LEN + 2 >= PAGE_SIZE)
+                       break;
+               cnt += sprintf(s + cnt, "%s%s", cnt ? " " : "", g->name);
+       }
+
+       cnt += sprintf(s + cnt, "\n");
+
+       mutex_unlock(&edp_lock);
+
+       return cnt;
+}
+
+static const struct sysfs_ops edp_sysfs_ops = {
+       .show = governors_show
+};
+
+static struct attribute attr_governors = {
+       .name = "governors",
+       .mode = 0444
+};
+
+static struct attribute *edp_attrs[] = {
+       &attr_governors,
+       NULL
+};
+
+static struct kobj_type ktype_edp = {
+       .sysfs_ops = &edp_sysfs_ops,
+       .default_attrs = edp_attrs
+};
+
+static int __init edp_sysfs_init(void)
+{
+       return kobject_init_and_add(&edp_kobj, &ktype_edp, power_kobj, "edp");
+}
+
+MODULE_LICENSE("GPL");
+module_init(edp_sysfs_init);
index 9cc59f3..ef114e7 100644 (file)
@@ -36,6 +36,7 @@ struct edp_manager {
        struct edp_governor *gov;
        struct work_struct work;
        unsigned int num_denied;
+       struct kobject *kobj;
 };
 
 /*
@@ -74,6 +75,7 @@ struct edp_client {
        unsigned int ithreshold;
        unsigned int num_borrowers;
        unsigned int num_loans;
+       struct kobject *kobj;
 
        /* governor internal */
        unsigned int gwt;