Initialize Tizen 2.3
[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/data.h"
27 #include "core/devices.h"
28 #include "core/edbus-handler.h"
29 #include "core/common.h"
30 #include "core/device-notifier.h"
31 #include "proc/proc-handler.h"
32
33 #define PREDEF_SET_MAX_FREQUENCY        "set_max_frequency"
34 #define PREDEF_SET_MIN_FREQUENCY        "set_min_frequency"
35 #define PREDEF_RELEASE_MAX_FREQUENCY    "release_max_frequency"
36 #define PREDEF_RELEASE_MIN_FREQUENCY    "release_min_frequency"
37 #define PREDEF_CPU_COUNT        "set_cpu_count"
38
39 #define POWER_SAVING_CPU_FREQ_RATE      (0.7)
40
41 #define DEFAULT_MAX_CPU_FREQ            1200000
42 #define DEFAULT_MIN_CPU_FREQ            100000
43
44 enum emergency_type {
45         EMERGENCY_UNLOCK = 0,
46         EMERGENCY_LOCK = 1,
47 };
48
49 static int max_cpu_freq_limit = -1;
50 static int min_cpu_freq_limit = -1;
51 static int cur_max_cpu_freq = INT_MAX;
52 static int cur_min_cpu_freq = INT_MIN;
53 static int power_saving_freq = -1;
54
55 static dd_list *max_cpu_freq_list;
56 static dd_list *min_cpu_freq_list;
57
58 static int cpu_number_limit = -1;
59 static int cur_cpu_number = INT_MAX;
60 static dd_list *cpu_number_list;
61
62 struct cpu_freq_entry {
63         int pid;
64         int freq;
65 };
66
67 struct cpu_number_entry {
68         int pid;
69         int number;
70 };
71
72 static int is_entry_enable(int pid)
73 {
74         char pid_path[PATH_MAX];
75
76         snprintf(pid_path, PATH_MAX, "/proc/%d", pid);
77         if (access(pid_path, F_OK) < 0) {
78                 return 0;
79         }
80
81         return 1;
82 }
83
84 static int write_min_cpu_freq(int freq)
85 {
86         int ret;
87
88         ret = device_set_property(DEVICE_TYPE_CPU, PROP_CPU_SCALING_MIN_FREQ, freq);
89         if (ret < 0) {
90                 _E("set cpufreq min freq write error: %s", strerror(errno));
91                 return ret;
92         }
93
94         return 0;
95 }
96
97 static int write_max_cpu_freq(int freq)
98 {
99         int ret;
100
101         ret = device_set_property(DEVICE_TYPE_CPU, PROP_CPU_SCALING_MAX_FREQ, freq);
102         if (ret < 0) {
103                 _E("set cpufreq max freq write error: %s", strerror(errno));
104                 return ret;
105         }
106
107         return 0;
108 }
109
110 static int remove_entry_from_min_cpu_freq_list(int pid)
111 {
112         dd_list *tmp;
113         struct cpu_freq_entry *entry;
114
115         cur_min_cpu_freq = INT_MIN;
116
117         DD_LIST_FOREACH(min_cpu_freq_list, tmp, entry) {
118                 if ((!is_entry_enable(entry->pid)) || (entry->pid == pid)) {
119                         DD_LIST_REMOVE(min_cpu_freq_list, entry);
120                         free(entry);
121                         continue;
122                 }
123                 if (entry->freq > cur_min_cpu_freq) {
124                         cur_min_cpu_freq = entry->freq;
125                 }
126         }
127
128         return 0;
129 }
130
131 static int remove_entry_from_max_cpu_freq_list(int pid)
132 {
133         dd_list *tmp;
134         struct cpu_freq_entry *entry;
135
136         cur_max_cpu_freq = INT_MAX;
137
138         DD_LIST_FOREACH(max_cpu_freq_list, tmp, entry) {
139                 if ((!is_entry_enable(entry->pid)) || (entry->pid == pid)) {
140                         DD_LIST_REMOVE(max_cpu_freq_list, entry);
141                         free(entry);
142                         continue;
143                 }
144                 if (entry->freq < cur_max_cpu_freq) {
145                         cur_max_cpu_freq = entry->freq;
146                 }
147         }
148
149         return 0;
150 }
151
152 int release_max_frequency_action(int argc, char **argv)
153 {
154         int r;
155
156         if (argc < 1)
157                 return -EINVAL;
158
159         r = remove_entry_from_max_cpu_freq_list(atoi(argv[0]));
160         if (r < 0) {
161                 _E("Remove entry failed");
162                 return r;
163         }
164
165         if (cur_max_cpu_freq == INT_MAX)
166                 cur_max_cpu_freq = max_cpu_freq_limit;
167
168         r = write_max_cpu_freq(cur_max_cpu_freq);
169         if (r < 0) {
170                 _E("Write freq failed");
171                 return r;
172         }
173
174         return 0;
175 }
176
177 int release_min_frequency_action(int argc, char **argv)
178 {
179         int r;
180
181         if (argc < 1)
182                 return -EINVAL;
183
184         r = remove_entry_from_min_cpu_freq_list(atoi(argv[0]));
185         if (r < 0) {
186                 _E("Remove entry failed");
187                 return r;
188         }
189
190         if (cur_min_cpu_freq == INT_MIN)
191                 cur_min_cpu_freq = min_cpu_freq_limit;
192
193         r = write_min_cpu_freq(cur_min_cpu_freq);
194         if (r < 0) {
195                 _E("Write entry failed");
196                 return r;
197         }
198
199         return 0;
200 }
201
202 static int add_entry_to_max_cpu_freq_list(int pid, int freq)
203 {
204         int r;
205         struct cpu_freq_entry *entry;
206
207         r = remove_entry_from_max_cpu_freq_list(pid);
208         if (r < 0) {
209                 _E("Remove duplicated entry failed");
210         }
211
212         entry = malloc(sizeof(struct cpu_freq_entry));
213         if (!entry) {
214                 _E("Malloc failed");
215                 return -ENOMEM;
216         }
217
218         entry->pid = pid;
219         entry->freq = freq;
220
221         DD_LIST_PREPEND(max_cpu_freq_list, entry);
222         if (!max_cpu_freq_list) {
223                 _E("eina_list_prepend failed");
224                 return -ENOSPC;
225         }
226         if (freq < cur_max_cpu_freq) {
227                 cur_max_cpu_freq = freq;
228         }
229         return 0;
230 }
231
232 static int add_entry_to_min_cpu_freq_list(int pid, int freq)
233 {
234         int r;
235         struct cpu_freq_entry *entry;
236
237         r = remove_entry_from_min_cpu_freq_list(pid);
238         if (r < 0) {
239                 _E("Remove duplicated entry failed");
240         }
241
242         entry = malloc(sizeof(struct cpu_freq_entry));
243         if (!entry) {
244                 _E("Malloc failed");
245                 return -ENOMEM;
246         }
247
248         entry->pid = pid;
249         entry->freq = freq;
250
251         DD_LIST_PREPEND(min_cpu_freq_list, entry);
252         if (!min_cpu_freq_list) {
253                 _E("eina_list_prepend failed");
254                 return -ENOSPC;
255         }
256         if (freq > cur_min_cpu_freq) {
257                 cur_min_cpu_freq = freq;
258         }
259         return 0;
260 }
261
262 int set_max_frequency_action(int argc, char **argv)
263 {
264         int r;
265
266         if (argc < 2)
267                 return -EINVAL;
268
269         r = add_entry_to_max_cpu_freq_list(atoi(argv[0]), atoi(argv[1]));
270         if (r < 0) {
271                 _E("Add entry failed");
272                 return r;
273         }
274
275         r = write_max_cpu_freq(cur_max_cpu_freq);
276         if (r < 0) {
277                 _E("Write entry failed");
278                 return r;
279         }
280
281         return 0;
282 }
283
284 int set_min_frequency_action(int argc, char **argv)
285 {
286         int r;
287
288         if (argc < 2)
289                 return -EINVAL;
290
291         r = add_entry_to_min_cpu_freq_list(atoi(argv[0]), atoi(argv[1]));
292         if (r < 0) {
293                 _E("Add entry failed");
294                 return r;
295         }
296
297         r = write_min_cpu_freq(cur_min_cpu_freq);
298         if (r < 0) {
299                 _E("Write entry failed");
300                 return r;
301         }
302
303         return 0;
304 }
305
306 static int power_saving_cpu_cb(keynode_t *key_nodes, void *data)
307 {
308         int ret = 0;
309         int val = 0;
310         int power_saving_cpu_stat = -1;
311
312         power_saving_cpu_stat = vconf_keynode_get_bool(key_nodes);
313         if (power_saving_cpu_stat == 1) {
314                 val = 1;
315                 ret = add_entry_to_max_cpu_freq_list(getpid(), power_saving_freq);
316                 if (ret < 0) {
317                         _E("Add entry failed");
318                         goto out;
319                 }
320         } else {
321                 ret = remove_entry_from_max_cpu_freq_list(getpid());
322                 if (ret < 0) {
323                         _E("Remove entry failed");
324                         goto out;
325                 }
326                 if (cur_max_cpu_freq == INT_MAX)
327                         cur_max_cpu_freq = max_cpu_freq_limit;
328         }
329         ret = write_max_cpu_freq(cur_max_cpu_freq);
330         if (ret < 0)
331                 _E("Write failed");
332 out:
333         device_notify(DEVICE_NOTIFIER_PMQOS_POWERSAVING, (void*)val);
334         return ret;
335 }
336
337 static void set_emergency_limit(void)
338 {
339         int ret, val;
340
341         ret = vconf_get_int(VCONFKEY_SETAPPL_PSMODE, &val);
342         if (ret < 0) {
343                 _E("failed to get vconf key");
344                 return;
345         }
346         if (val != SETTING_PSMODE_EMERGENCY)
347                 return;
348
349         val = EMERGENCY_LOCK;
350         device_notify(DEVICE_NOTIFIER_PMQOS_EMERGENCY, (void*)val);
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 int set_cpu_number_action(int argc, char **argv)
489 {
490         int r;
491
492         if(cur_siop_level() != 0)
493                 return -EINVAL;
494
495         if (argc == 1) {// release cpu number
496                 r = remove_entry_from_cpu_number_list(atoi(argv[0]));
497                 if (r < 0) {
498                         _E("Remove entry failed");
499                         return r;
500                 }
501
502                 if (cur_cpu_number == INT_MAX)
503                         cur_cpu_number = cpu_number_limit;
504
505                 r = write_cpu_number(cur_cpu_number);
506                 if (r < 0) {
507                         _E("Write cpu number failed");
508                         return r;
509                 }
510         } else if (argc ==2) {//set cpu number
511                 r = add_entry_to_cpu_number_list(atoi(argv[0]), atoi(argv[1]));
512                 if (r < 0) {
513                         _E("Add entry failed");
514                         return r;
515                 }
516
517                 r = write_cpu_number(cur_cpu_number);
518                 if (r < 0) {
519                         _E("Write entry failed");
520                         return r;
521                 }
522         }
523         return 0;
524 }
525
526 static int booting_done(void *data)
527 {
528         set_freq_limit();
529         set_emergency_limit();
530         return 0;
531 }
532
533 static DBusMessage *dbus_cpu_handler(E_DBus_Object *obj, DBusMessage *msg)
534 {
535         DBusError err;
536         DBusMessageIter iter;
537         DBusMessage *reply;
538         pid_t pid;
539         int ret;
540         int argc;
541         char *type_str;
542         char *argv[2];
543
544         dbus_error_init(&err);
545
546         if (!dbus_message_get_args(msg, &err,
547                     DBUS_TYPE_STRING, &type_str,
548                     DBUS_TYPE_INT32, &argc,
549                     DBUS_TYPE_STRING, &argv[0],
550                     DBUS_TYPE_STRING, &argv[1], DBUS_TYPE_INVALID)) {
551                 _E("there is no message");
552                 ret = -EINVAL;
553                 goto out;
554         }
555
556         if (argc < 0) {
557                 _E("message is invalid!");
558                 ret = -EINVAL;
559                 goto out;
560         }
561
562         pid = get_edbus_sender_pid(msg);
563         if (kill(pid, 0) == -1) {
564                 _E("%d process does not exist, dbus ignored!", pid);
565                 ret = -ESRCH;
566                 goto out;
567         }
568
569         if (strncmp(type_str, PREDEF_SET_MAX_FREQUENCY, strlen(PREDEF_SET_MAX_FREQUENCY)) == 0)
570                 ret = set_max_frequency_action(argc, (char **)&argv);
571         else if (strncmp(type_str, PREDEF_SET_MIN_FREQUENCY, strlen(PREDEF_SET_MIN_FREQUENCY)) == 0)
572                 ret = set_min_frequency_action(argc, (char **)&argv);
573         else if (strncmp(type_str, PREDEF_RELEASE_MAX_FREQUENCY, strlen(PREDEF_RELEASE_MAX_FREQUENCY)) == 0)
574                 ret = release_max_frequency_action(argc, (char **)&argv);
575         else if (strncmp(type_str, PREDEF_RELEASE_MIN_FREQUENCY, strlen(PREDEF_RELEASE_MIN_FREQUENCY)) == 0)
576                 ret = release_min_frequency_action(argc, (char **)&argv);
577 out:
578         reply = dbus_message_new_method_return(msg);
579         dbus_message_iter_init_append(reply, &iter);
580         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
581
582         return reply;
583 }
584
585 static const struct edbus_method edbus_methods[] = {
586         { PREDEF_SET_MAX_FREQUENCY,     "siss", "i", dbus_cpu_handler },
587         { PREDEF_SET_MIN_FREQUENCY,     "siss", "i", dbus_cpu_handler },
588         { PREDEF_RELEASE_MAX_FREQUENCY, "siss", "i", dbus_cpu_handler },
589         { PREDEF_RELEASE_MIN_FREQUENCY, "siss", "i", dbus_cpu_handler },
590 };
591
592 static void cpu_init(void *data)
593 {
594         int ret;
595
596         register_notifier(DEVICE_NOTIFIER_BOOTING_DONE, booting_done);
597         ret = register_edbus_method(DEVICED_PATH_SYSNOTI, edbus_methods, ARRAY_SIZE(edbus_methods));
598         if (ret < 0)
599                 _E("fail to init edbus method(%d)", ret);
600         set_cpu_number_limit();
601         register_action(PREDEF_SET_MAX_FREQUENCY, set_max_frequency_action, NULL, NULL);
602         register_action(PREDEF_SET_MIN_FREQUENCY, set_min_frequency_action, NULL, NULL);
603         register_action(PREDEF_RELEASE_MAX_FREQUENCY, release_max_frequency_action, NULL, NULL);
604         register_action(PREDEF_RELEASE_MIN_FREQUENCY, release_min_frequency_action, NULL, NULL);
605         register_action(PREDEF_CPU_COUNT, set_cpu_number_action, NULL, NULL);
606
607         vconf_notify_key_changed(VCONFKEY_SETAPPL_PWRSV_CUSTMODE_CPU, (void *)power_saving_cpu_cb, NULL);
608         vconf_notify_key_changed(VCONFKEY_SETAPPL_PSMODE, (void *)emergency_cpu_cb, NULL);
609 }
610
611 static const struct device_ops cpu_device_ops = {
612         .priority = DEVICE_PRIORITY_NORMAL,
613         .name     = "cpu",
614         .init     = cpu_init,
615 };
616
617 DEVICE_OPS_REGISTER(&cpu_device_ops)