cpuquiet: Update stats only on successful operations
[firefly-linux-kernel-4.4.55.git] / drivers / cpuquiet / driver.c
1 /*
2  * Copyright (c) 2012 NVIDIA CORPORATION.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16  *
17  */
18
19 #include <linux/mutex.h>
20 #include <linux/module.h>
21 #include <linux/cpuquiet.h>
22 #include <linux/cpu.h>
23 #include <linux/jiffies.h>
24 #include <linux/slab.h>
25 #include <asm/cputime.h>
26
27 #include "cpuquiet.h"
28
29 struct cpuquiet_cpu_stat {
30         cputime64_t time_up_total;
31         u64 last_update;
32         unsigned int up_down_count;
33         struct kobject cpu_kobject;
34 };
35
36 struct cpu_attribute {
37         struct attribute attr;
38         enum { up_down_count, time_up_total } type;
39 };
40
41 static struct cpuquiet_driver *cpuquiet_curr_driver;
42 struct cpuquiet_cpu_stat *stats;
43
44 #define CPU_ATTRIBUTE(_name) \
45         static struct cpu_attribute _name ## _attr = {                  \
46                 .attr =  {.name = __stringify(_name), .mode = 0444 },   \
47                 .type   = _name,                                        \
48 }
49
50 CPU_ATTRIBUTE(up_down_count);
51 CPU_ATTRIBUTE(time_up_total);
52
53 static struct attribute *cpu_attributes[] = {
54         &up_down_count_attr.attr,
55         &time_up_total_attr.attr,
56         NULL,
57 };
58
59 static void stats_update(struct cpuquiet_cpu_stat *stat, bool up)
60 {
61         u64 cur_jiffies = get_jiffies_64();
62         bool was_up = stat->up_down_count & 0x1;
63
64         if (was_up)
65                 stat->time_up_total = cputime64_add(stat->time_up_total,
66                         cputime64_sub(cur_jiffies, stat->last_update));
67
68         if (was_up != up)
69                 stat->up_down_count++;
70
71         stat->last_update = cur_jiffies;
72 }
73
74 int cpuquiet_quiesence_cpu(unsigned int cpunumber)
75 {
76         int err = -EPERM;
77
78         if (cpuquiet_curr_driver && cpuquiet_curr_driver->quiesence_cpu)
79                 err = cpuquiet_curr_driver->quiesence_cpu(cpunumber);
80
81         if (!err)
82                 stats_update(stats + cpunumber, 0);
83
84         return err;
85 }
86 EXPORT_SYMBOL(cpuquiet_quiesence_cpu);
87
88 int cpuquiet_wake_cpu(unsigned int cpunumber)
89 {
90         int err = -EPERM;
91
92         if (cpuquiet_curr_driver && cpuquiet_curr_driver->wake_cpu)
93                 err = cpuquiet_curr_driver->wake_cpu(cpunumber);
94
95         if (!err)
96                 stats_update(stats + cpunumber, 1);
97
98         return err;
99 }
100 EXPORT_SYMBOL(cpuquiet_wake_cpu);
101
102 static ssize_t stats_sysfs_show(struct kobject *kobj,
103                         struct attribute *attr, char *buf)
104 {
105         struct cpu_attribute *cattr =
106                 container_of(attr, struct cpu_attribute, attr);
107         struct cpuquiet_cpu_stat *stat  =
108                 container_of(kobj, struct cpuquiet_cpu_stat, cpu_kobject);
109         ssize_t len = 0;
110         bool was_up = stat->up_down_count & 0x1;
111
112         stats_update(stat, was_up);
113
114         switch (cattr->type) {
115         case up_down_count:
116                 len = sprintf(buf, "%u\n", stat->up_down_count);
117                 break;
118         case time_up_total:
119                 len =  sprintf(buf, "%llu\n", stat->time_up_total);
120                 break;
121         }
122
123         return len;
124 }
125
126 static const struct sysfs_ops stats_sysfs_ops = {
127         .show = stats_sysfs_show,
128 };
129
130 static struct kobj_type ktype_cpu_stats = {
131         .sysfs_ops = &stats_sysfs_ops,
132         .default_attrs = cpu_attributes,
133 };
134
135 int cpuquiet_register_driver(struct cpuquiet_driver *drv)
136 {
137         int err = -EBUSY;
138         unsigned int cpu;
139         struct sys_device *sys_dev;
140         u64 cur_jiffies;
141
142         if (!drv)
143                 return -EINVAL;
144
145         stats = kzalloc(nr_cpu_ids * sizeof(*stats), GFP_KERNEL);
146         if (!stats)
147                 return -ENOMEM;
148
149         for_each_possible_cpu(cpu) {
150                 cur_jiffies = get_jiffies_64();
151                 stats[cpu].last_update = cur_jiffies;
152                 if (cpu_online(cpu))
153                         stats[cpu].up_down_count = 1;
154                 sys_dev = get_cpu_sysdev(cpu);
155                 if (sys_dev) {
156                         cpuquiet_add_dev(sys_dev, cpu);
157                         cpuquiet_cpu_kobject_init(&stats[cpu].cpu_kobject,
158                                         &ktype_cpu_stats, "stats", cpu);
159                 }
160         }
161
162         mutex_lock(&cpuquiet_lock);
163         if (!cpuquiet_curr_driver) {
164                 err = 0;
165                 cpuquiet_curr_driver = drv;
166                 cpuquiet_switch_governor(cpuquiet_get_first_governor());
167         }
168         mutex_unlock(&cpuquiet_lock);
169
170         return err;
171 }
172 EXPORT_SYMBOL(cpuquiet_register_driver);
173
174 struct cpuquiet_driver *cpuquiet_get_driver(void)
175 {
176         return cpuquiet_curr_driver;
177 }
178
179 void cpuquiet_unregister_driver(struct cpuquiet_driver *drv)
180 {
181         unsigned int cpu;
182
183         if (drv != cpuquiet_curr_driver) {
184                 WARN(1, "invalid cpuquiet_unregister_driver(%s)\n",
185                         drv->name);
186                 return;
187         }
188
189         /* stop current governor first */
190         cpuquiet_switch_governor(NULL);
191
192         mutex_lock(&cpuquiet_lock);
193         cpuquiet_curr_driver = NULL;
194
195         for_each_possible_cpu(cpu) {
196                 kobject_put(&stats[cpu].cpu_kobject);
197                 cpuquiet_remove_dev(cpu);
198         }
199
200         mutex_unlock(&cpuquiet_lock);
201 }
202 EXPORT_SYMBOL(cpuquiet_unregister_driver);