EDP: allow reducing manager cap on the fly
[linux-2.6.git] / drivers / edp / edp_debug.c
1 /*
2  * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope 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
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  */
16
17 #include <linux/kernel.h>
18 #include <linux/module.h>
19 #include <linux/edp.h>
20 #include <linux/debugfs.h>
21 #include "edp_internal.h"
22
23 struct dentry *edp_debugfs_dir;
24
25 /*
26  * Reducing the cap is tricky - we might require throttling of other
27  * clients (therefore, involving the governor). So we will fool the
28  * framework by using a dummy client that has a single E-state (E0)
29  * equalling the reduction.
30  */
31 static int reduce_cap(struct edp_manager *m, unsigned int new_max)
32 {
33         int r = 0;
34         unsigned int delta = m->max - new_max;
35         unsigned int remain;
36         struct edp_client c = {
37                 .name = ".",
38                 .states = &delta,
39                 .num_states = 1,
40                 .e0_index = 0,
41                 .max_borrowers = 0,
42                 .priority = EDP_MIN_PRIO
43         };
44
45         r = register_client(m, &c);
46         if (r)
47                 return r;
48
49         r = edp_update_client_request_unlocked(&c, 0, NULL);
50         if (r)
51                 return r;
52
53         remain = m->remaining;
54         r = unregister_client(&c);
55         if (r)
56                 return r;
57
58         m->remaining = remain;
59         m->max = new_max;
60         return 0;
61 }
62
63 static int __manager_cap_set(struct edp_manager *m, unsigned int new_max)
64 {
65         if (new_max >= m->max) {
66                 m->remaining += new_max - m->max;
67                 m->max = new_max;
68                 schedule_promotion(m);
69                 return 0;
70         }
71
72         return reduce_cap(m, new_max);
73 }
74
75 static int manager_status_show(struct seq_file *file, void *data)
76 {
77         struct edp_manager *m;
78         struct edp_client *c;
79
80         if (!file->private)
81                 return -ENODEV;
82
83         m = file->private;
84
85         mutex_lock(&edp_lock);
86
87         seq_printf(file, "cap      : %u\n", m->max);
88         seq_printf(file, "remaining: %u\n", m->remaining);
89
90         seq_printf(file, "------------------------------------------\n");
91         seq_printf(file, "%-16s %3s %5s %7s %7s\n",
92                         "client", "pri", "E0", "request", "current");
93         seq_printf(file, "------------------------------------------\n");
94
95         list_for_each_entry(c, &m->clients, link)
96                 seq_printf(file, "%-16s %3d %5u %7u %7u\n", c->name,
97                                 c->priority, e0_level(c), req_level(c),
98                                 cur_level(c));
99
100         mutex_unlock(&edp_lock);
101         return 0;
102 }
103
104 static int manager_status_open(struct inode *inode, struct file *file)
105 {
106         return single_open(file, manager_status_show, inode->i_private);
107 }
108
109 static const struct file_operations manager_status_fops = {
110         .open = manager_status_open,
111         .read = seq_read,
112 };
113
114 static int manager_cap_set(void *data, u64 val)
115 {
116         struct edp_manager *m = data;
117         int r;
118
119         mutex_lock(&edp_lock);
120         r = __manager_cap_set(m, val);
121         mutex_unlock(&edp_lock);
122         return r;
123 }
124
125 static int manager_cap_get(void *data, u64 *val)
126 {
127         struct edp_manager *m = data;
128
129         mutex_lock(&edp_lock);
130         *val = m->max;
131         mutex_unlock(&edp_lock);
132         return 0;
133 }
134
135 DEFINE_SIMPLE_ATTRIBUTE(manager_cap_fops, manager_cap_get,
136                 manager_cap_set, "%lld\n");
137
138 void manager_add_dentry(struct edp_manager *m)
139 {
140         struct dentry *d;
141
142         if (!edp_debugfs_dir)
143                 return;
144
145         d = debugfs_create_dir(m->name, edp_debugfs_dir);
146         if (IS_ERR_OR_NULL(d))
147                 return;
148
149         m->dentry = d;
150
151         d = debugfs_create_file("status", S_IRUGO, m->dentry, m,
152                         &manager_status_fops);
153         WARN_ON(IS_ERR_OR_NULL(d));
154
155         d = debugfs_create_file("cap", S_IRUGO | S_IWUSR, m->dentry, m,
156                         &manager_cap_fops);
157         WARN_ON(IS_ERR_OR_NULL(d));
158 }
159
160 void manager_remove_dentry(struct edp_manager *m)
161 {
162         debugfs_remove_recursive(m->dentry);
163         m->dentry = NULL;
164 }
165
166 static int __client_current_set(struct edp_client *c, unsigned int new)
167 {
168         struct edp_manager *m;
169         unsigned int nl;
170         unsigned int cl;
171
172         if (new >= c->num_states)
173                 return -EINVAL;
174
175         nl = c->states[new];
176         cl = cur_level(c);
177         m = c->manager;
178
179         if (nl > cl && nl - cl > m->remaining)
180                 return -EBUSY;
181
182         c->cur = c->states + new;
183         c->req = c->states + new;
184
185         if (nl < cl) {
186                 m->remaining += cl - nl;
187                 if (c->throttle)
188                         c->throttle(new, c->private_data);
189                 schedule_promotion(m);
190         } else if (nl > cl) {
191                 m->remaining -= nl - cl;
192                 if (c->notify_promotion)
193                         c->notify_promotion(new, c->private_data);
194         }
195
196         return 0;
197 }
198
199 static int client_current_set(void *data, u64 val)
200 {
201         struct edp_client *c = data;
202         int r;
203
204         mutex_lock(&edp_lock);
205         r = __client_current_set(c, val);
206         mutex_unlock(&edp_lock);
207         return r;
208 }
209
210 static int client_current_get(void *data, u64 *val)
211 {
212         struct edp_client *c = data;
213
214         mutex_lock(&edp_lock);
215         *val = cur_level(c);
216         mutex_unlock(&edp_lock);
217         return 0;
218 }
219
220 DEFINE_SIMPLE_ATTRIBUTE(client_current_fops, client_current_get,
221                 client_current_set, "%lld\n");
222
223 void client_add_dentry(struct edp_client *c)
224 {
225         struct dentry *d;
226
227         if (!c->manager->dentry)
228                 return;
229
230         d = debugfs_create_dir(c->name, c->manager->dentry);
231         if (IS_ERR_OR_NULL(d)) {
232                 WARN_ON(1);
233                 return;
234         }
235
236         c->dentry = d;
237
238         d = debugfs_create_file("current", S_IRUGO | S_IWUSR, c->dentry,
239                         c, &client_current_fops);
240         WARN_ON(IS_ERR_OR_NULL(d));
241 }
242
243 void client_remove_dentry(struct edp_client *c)
244 {
245         debugfs_remove_recursive(c->dentry);
246         c->dentry = NULL;
247 }
248
249 static void dbg_update_request(struct edp_client *c, const unsigned int *r) {}
250 static void dbg_update_loans(struct edp_client *c) {}
251 static void dbg_promote(struct edp_manager *mgr) {}
252
253 static struct edp_governor dbg_governor = {
254         .name = "debug",
255         .owner = THIS_MODULE,
256         .update_request = dbg_update_request,
257         .update_loans = dbg_update_loans,
258         .promote = dbg_promote
259 };
260
261 static int __init debug_init(void)
262 {
263         struct dentry *d;
264
265         d = debugfs_create_dir("edp", NULL);
266         if (IS_ERR_OR_NULL(d)) {
267                 WARN_ON(1);
268                 return -EFAULT;
269         }
270
271         edp_debugfs_dir = d;
272         return edp_register_governor(&dbg_governor);
273 }
274 postcore_initcall(debug_init);