cpuquiet: sysfs interfaces
Peter De Schrijver [Fri, 30 Mar 2012 08:43:21 +0000 (11:43 +0300)]
Change-Id: Idb454f7380c48e2f4bab20e6ae51fef577b0f6c5
Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Reviewed-on: http://git-master/r/105267
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Sai Gurrappadi <sgurrappadi@nvidia.com>
Tested-by: Sai Gurrappadi <sgurrappadi@nvidia.com>
Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>

drivers/cpuquiet/sysfs.c [new file with mode: 0644]

diff --git a/drivers/cpuquiet/sysfs.c b/drivers/cpuquiet/sysfs.c
new file mode 100644 (file)
index 0000000..1e1c148
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2012 NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+#include <linux/cpuquiet.h>
+
+#include "cpuquiet.h"
+
+struct cpuquiet_dev {
+       unsigned int cpu;
+       struct kobject kobj;
+};
+
+struct cpuquiet_sysfs_attr {
+       struct attribute attr;
+       ssize_t (*show)(char *);
+       ssize_t (*store)(const char *, size_t count);
+};
+
+static struct kobject *cpuquiet_global_kobject;
+struct cpuquiet_dev *cpuquiet_cpu_devices[CONFIG_NR_CPUS];
+
+static ssize_t show_current_governor(char *buf)
+{
+       ssize_t ret;
+
+       mutex_lock(&cpuquiet_lock);
+
+       if (cpuquiet_curr_governor)
+               ret = sprintf(buf, "%s\n", cpuquiet_curr_governor->name);
+       else
+               ret = sprintf(buf, "none\n");
+
+       mutex_unlock(&cpuquiet_lock);
+
+       return ret;
+
+}
+
+static ssize_t store_current_governor(const char *buf, size_t count)
+{
+       char name[CPUQUIET_NAME_LEN];
+       struct cpuquiet_governor *gov;
+       int len = count, ret = -EINVAL;
+
+       if (!len || len >= sizeof(name))
+               return -EINVAL;
+
+       memcpy(name, buf, count);
+       name[len] = '\0';
+       if (name[len - 1] == '\n')
+               name[--len] = '\0';
+
+       mutex_lock(&cpuquiet_lock);
+       gov = cpuquiet_find_governor(name);
+       mutex_unlock(&cpuquiet_lock);
+
+       if (gov)
+               ret = cpuquiet_switch_governor(gov);
+
+       if (ret)
+               return ret;
+       else
+               return count;
+}
+
+static ssize_t available_governors_show(char *buf)
+{
+       ssize_t ret = 0, len;
+       struct cpuquiet_governor *gov;
+
+       mutex_lock(&cpuquiet_lock);
+       if (!list_empty(&cpuquiet_governors)) {
+               list_for_each_entry(gov, &cpuquiet_governors, governor_list) {
+                       len = sprintf(buf, "%s ", gov->name);
+                       buf += len;
+                       ret += len;
+               }
+               buf--;
+               *buf = '\n';
+       } else
+               ret = sprintf(buf, "none\n");
+
+       mutex_unlock(&cpuquiet_lock);
+
+       return ret;
+}
+
+struct cpuquiet_sysfs_attr attr_current_governor = __ATTR(current_governor,
+                       0644, show_current_governor, store_current_governor);
+struct cpuquiet_sysfs_attr attr_governors = __ATTR_RO(available_governors);
+
+
+static struct attribute *cpuquiet_default_attrs[] = {
+       &attr_current_governor.attr,
+       &attr_governors.attr,
+       NULL
+};
+
+static ssize_t cpuquiet_sysfs_show(struct kobject *kobj,
+                       struct attribute *attr, char *buf)
+{
+       struct cpuquiet_sysfs_attr *cattr =
+                       container_of(attr, struct cpuquiet_sysfs_attr, attr);
+
+       return cattr->show(buf);
+}
+
+static ssize_t cpuquiet_sysfs_store(struct kobject *kobj,
+                       struct attribute *attr, const char *buf, size_t count)
+{
+       struct cpuquiet_sysfs_attr *cattr =
+                       container_of(attr, struct cpuquiet_sysfs_attr, attr);
+
+       if (cattr->store)
+               return cattr->store(buf, count);
+
+       return -EINVAL;
+}
+
+static const struct sysfs_ops cpuquiet_sysfs_ops = {
+       .show = cpuquiet_sysfs_show,
+       .store = cpuquiet_sysfs_store,
+};
+
+static struct kobj_type ktype_cpuquiet_sysfs = {
+       .sysfs_ops = &cpuquiet_sysfs_ops,
+       .default_attrs = cpuquiet_default_attrs,
+};
+
+int cpuquiet_add_group(struct attribute_group *attrs)
+{
+       return sysfs_create_group(cpuquiet_global_kobject, attrs);
+}
+
+void cpuquiet_remove_group(struct attribute_group *attrs)
+{
+       sysfs_remove_group(cpuquiet_global_kobject, attrs);
+}
+
+int cpuquiet_kobject_init(struct kobject *kobj, struct kobj_type *type,
+                               char *name)
+{
+       int err;
+
+       err = kobject_init_and_add(kobj, type, cpuquiet_global_kobject, name);
+       if (!err)
+               kobject_uevent(kobj, KOBJ_ADD);
+
+       return err;
+}
+
+int cpuquiet_cpu_kobject_init(struct kobject *kobj, struct kobj_type *type,
+                               char *name, int cpu)
+{
+       int err;
+
+       err = kobject_init_and_add(kobj, type, &cpuquiet_cpu_devices[cpu]->kobj,
+                                       name);
+       if (!err)
+               kobject_uevent(kobj, KOBJ_ADD);
+
+       return err;
+}
+
+int cpuquiet_add_class_sysfs(struct sysdev_class *cls)
+{
+       int err;
+
+       cpuquiet_global_kobject = kzalloc(sizeof(*cpuquiet_global_kobject),
+                                               GFP_KERNEL);
+       if (!cpuquiet_global_kobject)
+               return -ENOMEM;
+
+       err = kobject_init_and_add(cpuquiet_global_kobject,
+                       &ktype_cpuquiet_sysfs, &cls->kset.kobj, "cpuquiet");
+       if (!err)
+               kobject_uevent(cpuquiet_global_kobject, KOBJ_ADD);
+
+       return err;
+}
+
+
+struct cpuquiet_attr {
+       struct attribute attr;
+       ssize_t (*show)(unsigned int, char *);
+       ssize_t (*store)(unsigned int, const char *, size_t count);
+};
+
+
+static ssize_t cpuquiet_state_show(struct kobject *kobj,
+       struct attribute *attr, char *buf)
+{
+       struct cpuquiet_attr *cattr = container_of(attr,
+                                       struct cpuquiet_attr, attr);
+       struct cpuquiet_dev *dev = container_of(kobj,
+                                       struct cpuquiet_dev, kobj);
+
+       return cattr->show(dev->cpu, buf);
+}
+
+static ssize_t cpuquiet_state_store(struct kobject *kobj,
+       struct attribute *attr, const char *buf, size_t count)
+{
+       struct cpuquiet_attr *cattr = container_of(attr,
+                                       struct cpuquiet_attr, attr);
+       struct cpuquiet_dev *dev = container_of(kobj,
+                                       struct cpuquiet_dev, kobj);
+
+       if (cattr->store)
+               return cattr->store(dev->cpu, buf, count);
+
+       return -EINVAL;
+}
+
+static ssize_t show_active(unsigned int cpu, char *buf)
+{
+       return sprintf(buf, "%u\n", cpu_online(cpu));
+}
+
+static ssize_t store_active(unsigned int cpu, const char *value, size_t count)
+{
+       unsigned int active;
+       int ret;
+
+       if (!cpuquiet_curr_governor->store_active)
+               return -EINVAL;
+
+       ret = sscanf(value, "%u", &active);
+       if (ret != 1)
+               return -EINVAL;
+
+       cpuquiet_curr_governor->store_active(cpu, active);
+
+       return count;
+}
+
+struct cpuquiet_attr attr_active = __ATTR(active, 0644, show_active,
+                                               store_active);
+
+static struct attribute *cpuquiet_default_cpu_attrs[] = {
+       &attr_active.attr,
+       NULL
+};
+
+static const struct sysfs_ops cpuquiet_cpu_sysfs_ops = {
+       .show = cpuquiet_state_show,
+       .store = cpuquiet_state_store,
+};
+
+static struct kobj_type ktype_cpuquiet = {
+       .sysfs_ops = &cpuquiet_cpu_sysfs_ops,
+       .default_attrs = cpuquiet_default_cpu_attrs,
+};
+
+void cpuquiet_add_dev(struct sys_device *sys_dev, unsigned int cpu)
+{
+       struct cpuquiet_dev *dev;
+       int err;
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       dev->cpu = cpu;
+       cpuquiet_cpu_devices[cpu] = dev;
+       err = kobject_init_and_add(&dev->kobj, &ktype_cpuquiet,
+                               &sys_dev->kobj, "cpuquiet");
+       if (!err)
+               kobject_uevent(&dev->kobj, KOBJ_ADD);
+}
+
+void cpuquiet_remove_dev(unsigned int cpu)
+{
+       kobject_put(cpuquiet_cpu_devices[cpu]);
+}