Signed-off-by: Daniel Lezcano <[email protected]>
---
 drivers/cpuidle/cpuidle.c |   8 +++
 drivers/cpuidle/sysfs.c   | 156 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/cpuidle.h   |   7 ++-
 3 files changed, 170 insertions(+), 1 deletion(-)

diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index bf42e17..66231a40 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -139,6 +139,14 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct 
cpuidle_driver *drv,
                 */
                dev->states_usage[entered_state].time += dev->last_residency;
                dev->states_usage[entered_state].usage++;
+               if (diff < dev->last_residency)
+                       atomic_inc(&dev->over_estimate);
+               else if (entered_state < (drv->state_count - 1) &&
+                        dev->last_residency <
+                        drv->states[entered_state + 1].target_residency)
+                       atomic_inc(&dev->under_estimate);
+               else
+                       atomic_inc(&dev->right_estimate);
        } else {
                dev->last_residency = 0;
        }
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c
index 97c5903..f446bd0 100644
--- a/drivers/cpuidle/sysfs.c
+++ b/drivers/cpuidle/sysfs.c
@@ -439,6 +439,154 @@ static void cpuidle_remove_state_sysfs(struct 
cpuidle_device *device)
                cpuidle_free_state_kobj(device, i);
 }
 
+#define kobj_to_stats_kobj(k) container_of(k, struct cpuidle_stats_kobj, kobj)
+#define attr_to_stats_attr(a) container_of(a, struct cpuidle_stats_attr, attr)
+
+#define define_show_stats_function(_name)                              \
+       static ssize_t show_stats_##_name(struct cpuidle_device *dev,   \
+                                         char *buf)                    \
+       {                                                               \
+               return sprintf(buf, "%d\n", atomic_read(&dev->_name));  \
+       }
+
+#define define_store_stats_function(_name)                             \
+       static ssize_t store_stats_##_name(struct cpuidle_device *dev,  \
+                                          const char *buf, size_t size) \
+       {                                                               \
+               unsigned long long value;                               \
+               int err;                                                \
+               if (!capable(CAP_SYS_ADMIN))                            \
+                       return -EPERM;                                  \
+               err = kstrtoull(buf, 0, &value);                        \
+               if (err)                                                \
+                       return err;                                     \
+                                                                       \
+               atomic_set(&dev->_name, value);                         \
+               return size;                                            \
+       }
+
+#define define_one_stats_rw(_name, show, store)                       \
+       static struct cpuidle_stats_attr attr_stats_##_name = \
+               __ATTR(_name, 0644, show, store)
+
+struct cpuidle_stats_kobj {
+       struct cpuidle_device *dev;
+       struct completion kobj_unregister;
+       struct kobject kobj;
+};
+
+struct cpuidle_stats_attr {
+       struct attribute attr;
+       ssize_t (*show)(struct cpuidle_device *, char *);
+       ssize_t (*store)(struct cpuidle_device *, const char *, size_t);
+};
+
+static void cpuidle_stats_sysfs_release(struct kobject *kobj)
+{
+       struct cpuidle_stats_kobj *stats_kobj = kobj_to_stats_kobj(kobj);
+       complete(&stats_kobj->kobj_unregister);
+}
+
+static ssize_t cpuidle_stats_show(struct kobject *kobj, struct attribute *attr,
+                                 char *buf)
+{
+       int ret = -EIO;
+       struct cpuidle_stats_kobj *stats_kobj = kobj_to_stats_kobj(kobj);
+       struct cpuidle_stats_attr *dattr = attr_to_stats_attr(attr);
+
+       if (dattr->show)
+               ret = dattr->show(stats_kobj->dev, buf);
+
+       return ret;
+}
+
+static ssize_t cpuidle_stats_store(struct kobject *kobj,
+                                  struct attribute *attr,
+                                  const char *buf, size_t size)
+{
+       int ret = -EIO;
+       struct cpuidle_stats_kobj *stats_kobj = kobj_to_stats_kobj(kobj);
+       struct cpuidle_stats_attr *dattr = attr_to_stats_attr(attr);
+
+       if (dattr->store)
+               ret = dattr->store(stats_kobj->dev, buf, size);
+
+       return ret;
+}
+
+define_show_stats_function(right_estimate);
+define_store_stats_function(right_estimate);
+
+define_show_stats_function(under_estimate);
+define_store_stats_function(under_estimate);
+
+define_show_stats_function(over_estimate);
+define_store_stats_function(over_estimate);
+
+define_one_stats_rw(right_estimate,
+                   show_stats_right_estimate,
+                   store_stats_right_estimate);
+
+define_one_stats_rw(under_estimate,
+                   show_stats_under_estimate,
+                   store_stats_under_estimate);
+
+define_one_stats_rw(over_estimate,
+                   show_stats_over_estimate,
+                   store_stats_over_estimate);
+
+static const struct sysfs_ops cpuidle_stats_sysfs_ops = {
+       .show = cpuidle_stats_show,
+       .store = cpuidle_stats_store,
+};
+
+static struct attribute *cpuidle_stats_default_attrs[] = {
+       &attr_stats_right_estimate.attr,
+       &attr_stats_under_estimate.attr,
+       &attr_stats_over_estimate.attr,
+       NULL
+};
+
+static struct kobj_type ktype_stats_cpuidle = {
+       .sysfs_ops = &cpuidle_stats_sysfs_ops,
+       .default_attrs = cpuidle_stats_default_attrs,
+       .release = cpuidle_stats_sysfs_release,
+};
+
+static int cpuidle_add_stats_sysfs(struct cpuidle_device *dev)
+{
+       struct cpuidle_stats_kobj *kstats;
+       struct cpuidle_device_kobj *kdev = dev->kobj_dev;
+       int ret;
+
+       kstats = kzalloc(sizeof(*kstats), GFP_KERNEL);
+       if (!kstats)
+               return -ENOMEM;
+
+       kstats->dev = dev;
+       init_completion(&kstats->kobj_unregister);
+
+       ret = kobject_init_and_add(&kstats->kobj, &ktype_stats_cpuidle,
+                                  &kdev->kobj, "stats");
+       if (ret) {
+               kfree(kstats);
+               return ret;
+       }
+
+       kobject_uevent(&kstats->kobj, KOBJ_ADD);
+       dev->kobj_stats = kstats;
+
+       return ret;
+}
+
+static void cpuidle_remove_stats_sysfs(struct cpuidle_device *dev)
+{
+       struct cpuidle_stats_kobj *kstats = dev->kobj_stats;
+       kobject_put(&kstats->kobj);
+       wait_for_completion(&kstats->kobj_unregister);
+       kfree(kstats);
+}
+
 #ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS
 #define kobj_to_driver_kobj(k) container_of(k, struct cpuidle_driver_kobj, 
kobj)
 #define attr_to_driver_attr(a) container_of(a, struct cpuidle_driver_attr, 
attr)
@@ -589,6 +737,13 @@ int cpuidle_add_device_sysfs(struct cpuidle_device *device)
        ret = cpuidle_add_driver_sysfs(device);
        if (ret)
                cpuidle_remove_state_sysfs(device);
+
+       ret = cpuidle_add_stats_sysfs(device);
+       if (ret) {
+               cpuidle_remove_driver_sysfs(device);
+               cpuidle_remove_state_sysfs(device);
+       }
+
        return ret;
 }
 
@@ -598,6 +753,7 @@ int cpuidle_add_device_sysfs(struct cpuidle_device *device)
  */
 void cpuidle_remove_device_sysfs(struct cpuidle_device *device)
 {
+       cpuidle_remove_stats_sysfs(device);
        cpuidle_remove_driver_sysfs(device);
        cpuidle_remove_state_sysfs(device);
 }
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index e99823f..c3b9bdd 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -44,7 +44,6 @@ struct cpuidle_state {
        int             power_usage; /* in mW */
        unsigned int    target_residency; /* in US */
        bool            disabled; /* disabled on all CPUs */
-
        int (*enter)    (struct cpuidle_device *dev,
                        struct cpuidle_driver *drv,
                        int index);
@@ -62,6 +61,7 @@ struct cpuidle_state {
 struct cpuidle_device_kobj;
 struct cpuidle_state_kobj;
 struct cpuidle_driver_kobj;
+struct cpuidle_stats_kobj;
 
 struct cpuidle_device {
        unsigned int            registered:1;
@@ -74,8 +74,13 @@ struct cpuidle_device {
        struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX];
        struct cpuidle_driver_kobj *kobj_driver;
        struct cpuidle_device_kobj *kobj_dev;
+       struct cpuidle_stats_kobj  *kobj_stats;
        struct list_head        device_list;
 
+       atomic_t right_estimate;
+       atomic_t under_estimate;
+       atomic_t over_estimate;
+
 #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
        int                     safe_state_index;
        cpumask_t               coupled_cpus;
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to