tizen 2.3 release
[framework/system/deviced.git] / src / cpu / cpu-handler.c
1 /*
2  * deviced
3  *
4  * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19
20 #include <fcntl.h>
21 #include <device-node.h>
22 #include <vconf.h>
23
24 #include "core/list.h"
25 #include "core/log.h"
26 #include "core/devices.h"
27 #include "core/edbus-handler.h"
28 #include "core/common.h"
29 #include "core/device-notifier.h"
30 #include "proc/proc-handler.h"
31
32 #define SET_MAX_FREQ    "set_max_frequency"
33 #define SET_MIN_FREQ    "set_min_frequency"
34 #define SET_FREQ_LEN    17
35
36 #define RELEASE_MAX_FREQ        "release_max_frequency"
37 #define RELEASE_MIN_FREQ        "release_min_frequency"
38 #define RELEASE_FREQ_LEN        21
39
40 #define POWER_SAVING_CPU_FREQ_RATE      (0.7)
41
42 #define DEFAULT_MAX_CPU_FREQ            1200000
43 #define DEFAULT_MIN_CPU_FREQ            100000
44
45 enum emergency_type {
46         EMERGENCY_UNLOCK = 0,
47         EMERGENCY_LOCK = 1,
48 };
49
50 static int max_cpu_freq_limit = -1;
51 static int min_cpu_freq_limit = -1;
52 static int cur_max_cpu_freq = INT_MAX;
53 static int cur_min_cpu_freq = INT_MIN;
54 static int power_saving_freq = -1;
55
56 static dd_list *max_cpu_freq_list;
57 static dd_list *min_cpu_freq_list;
58
59 static int cpu_number_limit = -1;
60 static int cur_cpu_number = INT_MAX;
61 static dd_list *cpu_number_list;
62
63 struct cpu_freq_entry {
64         int pid;
65         int freq;
66 };
67
68 struct cpu_number_entry {
69         int pid;
70         int number;
71 };
72
73 static int is_entry_enable(int pid)
74 {
75         char pid_path[PATH_MAX];
76
77         snprintf(pid_path, PATH_MAX, "/proc/%d", pid);
78         if (access(pid_path, F_OK) < 0) {
79                 return 0;
80         }
81
82         return 1;
83 }
84
85 static int write_min_cpu_freq(int freq)
86 {
87         int ret;
88
89         ret = device_set_property(DEVICE_TYPE_CPU, PROP_CPU_SCALING_MIN_FREQ, freq);
90         if (ret < 0) {
91                 _E("set cpufreq min freq write error: %s", strerror(errno));
92                 return ret;
93         }
94
95         return 0;
96 }
97
98 static int write_max_cpu_freq(int freq)
99 {
100         int ret;
101
102         ret = device_set_property(DEVICE_TYPE_CPU, PROP_CPU_SCALING_MAX_FREQ, freq);
103         if (ret < 0) {
104                 _E("set cpufreq max freq write error: %s", strerror(errno));
105                 return ret;
106         }
107
108         return 0;
109 }
110
111 static int remove_entry_from_min_cpu_freq_list(int pid)
112 {
113         dd_list *tmp;
114         struct cpu_freq_entry *entry;
115
116         cur_min_cpu_freq = INT_MIN;
117
118         DD_LIST_FOREACH(min_cpu_freq_list, tmp, entry) {
119                 if ((!is_entry_enable(entry->pid)) || (entry->pid == pid)) {
120                         DD_LIST_REMOVE(min_cpu_freq_list, entry);
121                         free(entry);
122                         continue;
123                 }
124                 if (entry->freq > cur_min_cpu_freq) {
125                         cur_min_cpu_freq = entry->freq;
126                 }
127         }
128
129         return 0;
130 }
131
132 static int remove_entry_from_max_cpu_freq_list(int pid)
133 {
134         dd_list *tmp;
135         struct cpu_freq_entry *entry;
136
137         cur_max_cpu_freq = INT_MAX;
138
139         DD_LIST_FOREACH(max_cpu_freq_list, tmp, entry) {
140                 if ((!is_entry_enable(entry->pid)) || (entry->pid == pid)) {
141                         DD_LIST_REMOVE(max_cpu_freq_list, entry);
142                         free(entry);
143                         continue;
144                 }
145                 if (entry->freq < cur_max_cpu_freq) {
146                         cur_max_cpu_freq = entry->freq;
147                 }
148         }
149
150         return 0;
151 }
152
153 int release_max_frequency_action(int argc, char **argv)
154 {
155         int r;
156
157         if (argc < 1)
158                 return -EINVAL;
159
160         r = remove_entry_from_max_cpu_freq_list(atoi(argv[0]));
161         if (r < 0) {
162                 _E("Remove entry failed");
163                 return r;
164         }
165
166         if (cur_max_cpu_freq == INT_MAX)
167                 cur_max_cpu_freq = max_cpu_freq_limit;
168
169         r = write_max_cpu_freq(cur_max_cpu_freq);
170         if (r < 0) {
171                 _E("Write freq failed");
172                 return r;
173         }
174
175         return 0;
176 }
177
178 int release_min_frequency_action(int argc, char **argv)
179 {
180         int r;
181
182         if (argc < 1)
183                 return -EINVAL;
184
185         r = remove_entry_from_min_cpu_freq_list(atoi(argv[0]));
186         if (r < 0) {
187                 _E("Remove entry failed");
188                 return r;
189         }
190
191         if (cur_min_cpu_freq == INT_MIN)
192                 cur_min_cpu_freq = min_cpu_freq_limit;
193
194         r = write_min_cpu_freq(cur_min_cpu_freq);
195         if (r < 0) {
196                 _E("Write entry failed");
197                 return r;
198         }
199
200         return 0;
201 }
202
203 static int add_entry_to_max_cpu_freq_list(int pid, int freq)
204 {
205         int r;
206         struct cpu_freq_entry *entry;
207
208         r = remove_entry_from_max_cpu_freq_list(pid);
209         if (r < 0) {
210                 _E("Remove duplicated entry failed");
211         }
212
213         entry = malloc(sizeof(struct cpu_freq_entry));
214         if (!entry) {
215                 _E("Malloc failed");
216                 return -ENOMEM;
217         }
218
219         entry->pid = pid;
220         entry->freq = freq;
221
222         DD_LIST_PREPEND(max_cpu_freq_list, entry);
223         if (!max_cpu_freq_list) {
224                 _E("eina_list_prepend failed");
225                 return -ENOSPC;
226         }
227         if (freq < cur_max_cpu_freq) {
228                 cur_max_cpu_freq = freq;
229         }
230         return 0;
231 }
232
233 static int add_entry_to_min_cpu_freq_list(int pid, int freq)
234 {
235         int r;
236         struct cpu_freq_entry *entry;
237
238         r = remove_entry_from_min_cpu_freq_list(pid);
239         if (r < 0) {
240                 _E("Remove duplicated entry failed");
241         }
242
243         entry = malloc(sizeof(struct cpu_freq_entry));
244         if (!entry) {
245                 _E("Malloc failed");
246                 return -ENOMEM;
247         }
248
249         entry->pid = pid;
250         entry->freq = freq;
251
252         DD_LIST_PREPEND(min_cpu_freq_list, entry);
253         if (!min_cpu_freq_list) {
254                 _E("eina_list_prepend failed");
255                 return -ENOSPC;
256         }
257         if (freq > cur_min_cpu_freq) {
258                 cur_min_cpu_freq = freq;
259         }
260         return 0;
261 }
262
263 int set_max_frequency_action(int argc, char **argv)
264 {
265         int r;
266
267         if (argc < 2)
268                 return -EINVAL;
269
270         r = add_entry_to_max_cpu_freq_list(atoi(argv[0]), atoi(argv[1]));
271         if (r < 0) {
272                 _E("Add entry failed");
273                 return r;
274         }
275
276         r = write_max_cpu_freq(cur_max_cpu_freq);
277         if (r < 0) {
278                 _E("Write entry failed");
279                 return r;
280         }
281
282         return 0;
283 }
284
285 int set_min_frequency_action(int argc, char **argv)
286 {
287         int r;
288
289         if (argc < 2)
290                 return -EINVAL;
291
292         r = add_entry_to_min_cpu_freq_list(atoi(argv[0]), atoi(argv[1]));
293         if (r < 0) {
294                 _E("Add entry failed");
295                 return r;
296         }
297
298         r = write_min_cpu_freq(cur_min_cpu_freq);
299         if (r < 0) {
300                 _E("Write entry failed");
301                 return r;
302         }
303
304         return 0;
305 }
306
307 static int power_saving_cpu_cb(keynode_t *key_nodes, void *data)
308 {
309         int ret = 0;
310         int val = 0;
311         int power_saving_cpu_stat = -1;
312
313         power_saving_cpu_stat = vconf_keynode_get_bool(key_nodes);
314         if (power_saving_cpu_stat == 1) {
315                 val = 1;
316                 ret = add_entry_to_max_cpu_freq_list(getpid(), power_saving_freq);
317                 if (ret < 0) {
318                         _E("Add entry failed");
319                         goto out;
320                 }
321         } else {
322                 ret = remove_entry_from_max_cpu_freq_list(getpid());
323                 if (ret < 0) {
324                         _E("Remove entry failed");
325                         goto out;
326                 }
327                 if (cur_max_cpu_freq == INT_MAX)
328                         cur_max_cpu_freq = max_cpu_freq_limit;
329         }
330         ret = write_max_cpu_freq(cur_max_cpu_freq);
331         if (ret < 0)
332                 _E("Write failed");
333 out:
334         device_notify(DEVICE_NOTIFIER_PMQOS_POWERSAVING, (void*)val);
335         return ret;
336 }
337
338 static void set_emergency_limit(void)
339 {
340         int ret, val;
341
342         ret = vconf_get_int(VCONFKEY_SETAPPL_PSMODE, &val);
343         if (ret < 0) {
344                 _E("failed to get vconf key");
345                 return;
346         }
347         if (val == SETTING_PSMODE_EMERGENCY) {
348                 val = EMERGENCY_LOCK;
349                 device_notify(DEVICE_NOTIFIER_PMQOS_EMERGENCY, (void*)val);
350         }
351 }
352
353 static int emergency_cpu_cb(keynode_t *key_nodes, void *data)
354 {
355         int val;
356
357         val = vconf_keynode_get_int(key_nodes);
358         if (val == SETTING_PSMODE_EMERGENCY)
359                 val = EMERGENCY_LOCK;
360         else
361                 val = EMERGENCY_UNLOCK;
362
363         device_notify(DEVICE_NOTIFIER_PMQOS_EMERGENCY, (void*)val);
364         return 0;
365 }
366
367 static void set_freq_limit(void)
368 {
369         int ret = 0;
370         int val = 0;
371         int power_saving_stat = -1;
372         int power_saving_cpu_stat = -1;
373
374         ret = device_get_property(DEVICE_TYPE_CPU, PROP_CPU_CPUINFO_MAX_FREQ,
375                         &max_cpu_freq_limit);
376         if (ret < 0) {
377                 _E("get cpufreq cpuinfo max readerror: %s", strerror(errno));
378                 max_cpu_freq_limit = DEFAULT_MAX_CPU_FREQ;
379         }
380
381         ret = device_get_property(DEVICE_TYPE_CPU, PROP_CPU_CPUINFO_MIN_FREQ,
382                         &min_cpu_freq_limit);
383         if (ret < 0) {
384                 _E("get cpufreq cpuinfo min readerror: %s", strerror(errno));
385                 min_cpu_freq_limit = DEFAULT_MIN_CPU_FREQ;
386         }
387         power_saving_freq = (int)(max_cpu_freq_limit * POWER_SAVING_CPU_FREQ_RATE);
388         _I("max(%d) , ps(%d), min(%d)",
389                 max_cpu_freq_limit,
390                 power_saving_freq,
391                 min_cpu_freq_limit);
392
393         ret = vconf_get_bool(VCONFKEY_SETAPPL_PWRSV_CUSTMODE_CPU,
394                         &power_saving_cpu_stat);
395         if (ret < 0) {
396                 _E("failed to get vconf key");
397                 return;
398         }
399         if (power_saving_cpu_stat != 1)
400                 return;
401         val = 1;
402         ret = add_entry_to_max_cpu_freq_list(getpid(), power_saving_freq);
403         if (ret < 0) {
404                 _E("Add entry failed");
405                 goto out;
406         }
407         ret = write_max_cpu_freq(cur_max_cpu_freq);
408         if (ret < 0)
409                 _E("Write entry failed");
410 out:
411         _I("init");
412         device_notify(DEVICE_NOTIFIER_PMQOS_POWERSAVING, (void*)val);
413 }
414
415 static void set_cpu_number_limit(void)
416 {
417         device_get_property(DEVICE_TYPE_CPU, PROP_CPU_ENABLE_MAX_NUMBER,
418                 &cpu_number_limit);
419 }
420
421 static int write_cpu_number(int number)
422 {
423         int ret;
424
425         ret = device_set_property(DEVICE_TYPE_CPU, PROP_CPU_ENABLE_MAX_NUMBER,
426                         number);
427         if (ret < 0) {
428                 _E("set cpu number max write error: %s", strerror(errno));
429                 return ret;
430         }
431
432         return 0;
433 }
434
435 static int remove_entry_from_cpu_number_list(int pid)
436 {
437         dd_list *tmp;
438         struct cpu_number_entry *entry;
439
440         cur_cpu_number = INT_MAX;
441
442         DD_LIST_FOREACH(cpu_number_list, tmp, entry) {
443                 if ((!is_entry_enable(entry->pid)) || (entry->pid == pid)) {
444                         DD_LIST_REMOVE(cpu_number_list, entry);
445                         free(entry);
446                         continue;
447                 }
448                 if (entry->number < cur_cpu_number) {
449                         cur_cpu_number = entry->number;
450                 }
451         }
452
453         return 0;
454 }
455
456 static int add_entry_to_cpu_number_list(int pid, int number)
457 {
458         int r;
459         struct cpu_number_entry *entry;
460
461         r = remove_entry_from_cpu_number_list(pid);
462         if (r < 0) {
463                 _E("Remove duplicated entry failed");
464         }
465
466
467
468         entry = malloc(sizeof(struct cpu_number_entry));
469         if (!entry) {
470                 _E("Malloc failed");
471                 return -ENOMEM;
472         }
473
474         entry->pid = pid;
475         entry->number = number;
476
477         DD_LIST_PREPEND(cpu_number_list, entry);
478         if (!cpu_number_list) {
479                 _E("eina_list_prepend failed");
480                 return -ENOSPC;
481         }
482         if (number < cur_cpu_number) {
483                 cur_cpu_number = number;
484         }
485         return 0;
486 }
487
488 static int booting_done(void *data)
489 {
490         set_freq_limit();
491         set_emergency_limit();
492         return 0;
493 }
494
495 static DBusMessage *dbus_cpu_handler(E_DBus_Object *obj, DBusMessage *msg)
496 {
497         DBusError err;
498         DBusMessageIter iter;
499         DBusMessage *reply;
500         pid_t pid;
501         int ret;
502         int argc;
503         char *type_str;
504         char *argv[2];
505
506         dbus_error_init(&err);
507
508         if (!dbus_message_get_args(msg, &err,
509                     DBUS_TYPE_STRING, &type_str,
510                     DBUS_TYPE_INT32, &argc,
511                     DBUS_TYPE_STRING, &argv[0],
512                     DBUS_TYPE_STRING, &argv[1], DBUS_TYPE_INVALID)) {
513                 _E("there is no message");
514                 ret = -EINVAL;
515                 goto out;
516         }
517
518         if (argc < 0) {
519                 _E("message is invalid!");
520                 ret = -EINVAL;
521                 goto out;
522         }
523
524         pid = get_edbus_sender_pid(msg);
525         if (kill(pid, 0) == -1) {
526                 _E("%d process does not exist, dbus ignored!", pid);
527                 ret = -ESRCH;
528                 goto out;
529         }
530
531         if (!strncmp(type_str, SET_MAX_FREQ, SET_FREQ_LEN))
532                 ret = set_max_frequency_action(argc, (char **)&argv);
533         else if (!strncmp(type_str, SET_MIN_FREQ, SET_FREQ_LEN))
534                 ret = set_min_frequency_action(argc, (char **)&argv);
535         else if (!strncmp(type_str, RELEASE_MAX_FREQ, RELEASE_FREQ_LEN))
536                 ret = release_max_frequency_action(argc, (char **)&argv);
537         else if (!strncmp(type_str, RELEASE_MIN_FREQ, RELEASE_FREQ_LEN))
538                 ret = release_min_frequency_action(argc, (char **)&argv);
539 out:
540         reply = dbus_message_new_method_return(msg);
541         dbus_message_iter_init_append(reply, &iter);
542         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
543
544         return reply;
545 }
546
547 static const struct edbus_method edbus_methods[] = {
548         { SET_MAX_FREQ,     "siss", "i", dbus_cpu_handler },
549         { SET_MIN_FREQ,     "siss", "i", dbus_cpu_handler },
550         { RELEASE_MAX_FREQ, "siss", "i", dbus_cpu_handler },
551         { RELEASE_MIN_FREQ, "siss", "i", dbus_cpu_handler },
552 };
553
554 static void cpu_init(void *data)
555 {
556         int ret;
557
558         register_notifier(DEVICE_NOTIFIER_BOOTING_DONE, booting_done);
559         ret = register_edbus_method(DEVICED_PATH_SYSNOTI, edbus_methods, ARRAY_SIZE(edbus_methods));
560         if (ret < 0)
561                 _E("fail to init edbus method(%d)", ret);
562         set_cpu_number_limit();
563
564         vconf_notify_key_changed(VCONFKEY_SETAPPL_PWRSV_CUSTMODE_CPU, (void *)power_saving_cpu_cb, NULL);
565         vconf_notify_key_changed(VCONFKEY_SETAPPL_PSMODE, (void *)emergency_cpu_cb, NULL);
566 }
567
568 static const struct device_ops cpu_device_ops = {
569         .priority = DEVICE_PRIORITY_NORMAL,
570         .name     = "cpu",
571         .init     = cpu_init,
572 };
573
574 DEVICE_OPS_REGISTER(&cpu_device_ops)