tizen 2.4 release
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / kernel / time / time_history.c
1 #include <linux/time_history.h>
2 #include <linux/time.h>
3 #include <linux/hrtimer.h>
4 #include <linux/alarmtimer.h>
5 #include <linux/sched.h>
6 #include <linux/proc_fs.h>
7 #include <linux/seq_file.h>
8 #include <linux/spinlock.h>
9
10 #ifdef CONFIG_SLP_KERNEL_ENG
11         #define TIME_LOG_MAX    (1000)
12         #define ALARM_LOG_MAX   (3000)
13 #else
14         #define TIME_LOG_MAX    (500)
15         #define ALARM_LOG_MAX   (1500)
16 #endif
17
18 #define ALARM_ID_MAX            (32)
19 #define OWNER_LEN                       (32)
20
21 #define for_each_alarm(_alarm_table, _alarm)     \
22         for (_alarm = _alarm_table;                                      \
23                 (_alarm < _alarm_table + ALARM_ID_MAX) && (_alarm->alarmid); \
24                         _alarm++)
25
26 struct time_log_entry {
27         u64 history_idx;
28         enum time_history_type history_type;
29         void *alarmid;
30         struct timespec realtime;
31         struct timespec monotonic;
32 #ifdef CONFIG_TIME_HISTORY_SAVE_BOOTTIME
33         struct timespec boottime;
34 #endif
35         struct timespec oldtime;
36         struct timespec newtime;
37         time_t tz_offset;
38         int err;
39         void *caller;
40         char comm[TASK_COMM_LEN + 1];
41         pid_t pid;
42 };
43
44 struct time_history_log_buf {
45         struct time_log_entry *buf;
46         int head;
47         int tail;
48         int buf_size;
49         u64 cnt;
50 };
51
52 struct alarm_info {
53         void *alarmid;
54         void *caller;
55         void *handler;
56         char owner[OWNER_LEN + 1];
57 };
58
59 struct time_history {
60         u64 history_cnt;
61         struct alarm_info alarm_table[ALARM_ID_MAX];
62         int alarmid_cnt;
63         spinlock_t lock;
64         char timezone[8];
65         char timezone_id[16];
66         int nitz_update;
67 };
68
69 static struct time_log_entry time_log_buf[TIME_LOG_MAX];
70 static struct time_log_entry alarm_log_buf[ALARM_LOG_MAX];
71
72 static struct time_history_log_buf time_log = {
73         .buf  = time_log_buf,
74         .buf_size = ARRAY_SIZE(time_log_buf),
75 };
76
77 static struct time_history_log_buf alarm_log = {
78         .buf  = alarm_log_buf,
79         .buf_size = ARRAY_SIZE(alarm_log_buf),
80 };
81
82 static struct time_history th_ctxt = {
83         .timezone = "+00:00",
84         .timezone_id = "UTC",
85         .nitz_update = -1,
86 };
87
88 static bool is_realtime(struct timespec *time)
89 {
90         const struct timespec realtime = {
91                 .tv_sec = 946684800, // 2000-01-01 00:00:00 //
92                 .tv_nsec = 0,
93         };
94         /*
95          * lhs == rhs: return  0
96          * lhs  > rhs: return >0
97          */
98         return (timespec_compare(time, &realtime) >= 0);
99 }
100
101 static void set_alarm_owner(struct alarm_info *alarm_info, void *caller)
102 {
103         const char *remove_str[] = {
104                 "_init",
105                 "_probe",
106                 "_register",
107         };
108         char *pos;
109         int i;
110
111         snprintf(alarm_info->owner, OWNER_LEN, "%pf", caller);
112
113         for (i = 0; i < ARRAY_SIZE(remove_str); i++) {
114                 pos = strstr(alarm_info->owner, remove_str[i]);
115                 if (pos && pos != alarm_info->owner)
116                         *pos = '\0';
117         }
118 }
119
120 static struct alarm_info *time_history_get_alarm_by_id(const void *alarmid)
121 {
122         struct alarm_info *alarm;
123
124         for_each_alarm(th_ctxt.alarm_table, alarm) {
125                 if (alarmid == alarm->alarmid)
126                         return alarm;
127         }
128
129         return NULL;
130 }
131
132 static int time_history_ringbuf_tail(struct time_history_log_buf *log_buf)
133 {
134         /* overwrite ring buf */
135         if (log_buf->cnt >= log_buf->buf_size)
136                 log_buf->head++;
137
138         if (log_buf->head >= log_buf->buf_size)
139                 log_buf->head = 0;
140
141         if (log_buf->cnt)
142                 log_buf->tail++;
143
144         if (log_buf->tail >= log_buf->buf_size)
145                 log_buf->tail = 0;
146
147         log_buf->cnt++;
148
149         return log_buf->tail;
150 }
151
152 static time_t time_history_timezone_offset(void)
153 {
154         int tz_hour, tz_min;
155
156         /* except first char '+' or '-' */
157         if (sscanf(&th_ctxt.timezone[1], "%d:%d", &tz_hour, &tz_min) < 0)
158                 return 0;
159
160         if (tz_hour == 0 && tz_min == 0)
161                 return 0;
162
163         if (th_ctxt.timezone[0] == '-') {
164                 tz_hour = 0 - tz_hour;
165                 tz_min  = 0 - tz_min;
166         }
167
168         return ((tz_hour * 3600) + (tz_min * 60));
169 }
170
171 static struct time_log_entry *time_history_init_log_entry(
172                 struct time_history_log_buf *log_buf)
173 {
174         struct time_log_entry *entry;
175         int tail;
176
177         tail = time_history_ringbuf_tail(log_buf);
178         entry = &log_buf->buf[tail];
179
180         memset(entry, 0, sizeof(struct time_log_entry));
181
182         entry->history_idx = th_ctxt.history_cnt;
183         ktime_get_ts(&entry->monotonic);
184         getnstimeofday(&entry->realtime);
185 #ifdef CONFIG_TIME_HISTORY_SAVE_BOOTTIME
186         get_monotonic_boottime(&entry->boottime);
187 #endif
188         entry->tz_offset = time_history_timezone_offset();
189
190         return entry;
191 }
192
193 #ifdef CONFIG_TIME_HISTORY_LOG_FILTER
194 static const char *alarm_owner_filter[] = {
195         "sec_battery",
196 };
197
198 static int time_history_check_log_filter(const void *alarmid)
199 {
200         struct alarm_info *alarm_info;
201         int i;
202
203         alarm_info = time_history_get_alarm_by_id(alarmid);
204         if (!alarm_info)
205                 return 0;
206
207         for (i = 0; i < ARRAY_SIZE(alarm_owner_filter); i++) {
208                 if (strcasecmp(alarm_owner_filter[i], alarm_info->owner) == 0)
209                         return 1;
210         }
211         return 0;
212 }
213 #endif /* CONFIG_TIME_HISTORY_LOG_FILTER */
214
215 static int time_history_insert_alarm_log(enum time_history_type type,
216                 const struct alarm *alarm, void *caller)
217 {
218         struct time_log_entry *entry;
219         unsigned long flags;
220
221 #ifdef CONFIG_TIME_HISTORY_LOG_FILTER
222         if (time_history_check_log_filter(&alarm->node.node) != 0)
223                 return 0;
224 #endif
225
226         spin_lock_irqsave(&th_ctxt.lock, flags);
227         entry = time_history_init_log_entry(&alarm_log);
228         entry->history_type = type;
229         entry->newtime = ktime_to_timespec(alarm->node.expires);
230         entry->caller = caller;
231         entry->alarmid = (void*)&alarm->node.node;
232         entry->pid = current->pid;
233         memcpy(entry->comm, current->comm, TASK_COMM_LEN);
234
235         th_ctxt.history_cnt++;
236         spin_unlock_irqrestore(&th_ctxt.lock, flags);
237
238         return 0;
239 }
240
241 static int time_history_insert_time_log(enum time_history_type type,
242                 const struct timespec *oldtime, const struct timespec *newtime,
243                 void *caller, int err)
244 {
245         struct time_log_entry *entry;
246         unsigned long flags;
247
248         spin_lock_irqsave(&th_ctxt.lock, flags);
249         entry = time_history_init_log_entry(&time_log);
250         entry->history_type = type;
251         if (type == TIME_HISTORY_TYPE_TIME_SET)
252                 entry->realtime = *oldtime;
253         entry->oldtime = *oldtime;
254         entry->newtime = *newtime;
255         entry->caller = caller;
256         entry->pid = current->pid;
257         entry->err = err;
258         memcpy(entry->comm, current->comm, TASK_COMM_LEN);
259
260         th_ctxt.history_cnt++;
261         spin_unlock_irqrestore(&th_ctxt.lock, flags);
262
263         return 0;
264 }
265
266 static int time_history_insert_timezone_log(char *old_tz, char *new_tz)
267 {
268         struct time_log_entry *entry;
269         unsigned long flags;
270
271         spin_lock_irqsave(&th_ctxt.lock, flags);
272         entry = time_history_init_log_entry(&time_log);
273         entry->history_type = TIME_HISTORY_TYPE_TIMEZONE_SET;
274         snprintf(entry->comm, sizeof(entry->comm) - 1, "%s > %s", old_tz, new_tz);
275
276         th_ctxt.history_cnt++;
277         spin_unlock_irqrestore(&th_ctxt.lock, flags);
278
279         return 0;
280 }
281
282 static int time_history_insert_setting_log(enum time_history_type type,
283                 int old, int new)
284 {
285         struct time_log_entry *entry;
286         unsigned long flags;
287
288         spin_lock_irqsave(&th_ctxt.lock, flags);
289         entry = time_history_init_log_entry(&time_log);
290         entry->history_type = type;
291         entry->oldtime.tv_sec = old;
292         entry->newtime.tv_sec = new;
293
294         th_ctxt.history_cnt++;
295         spin_unlock_irqrestore(&th_ctxt.lock, flags);
296
297         return 0;
298 }
299
300 void __time_history_alarm_init(const struct alarm *alarm, void *caller)
301 {
302         struct alarm_info *alarm_info;
303
304         if (th_ctxt.alarmid_cnt >= ALARM_ID_MAX) {
305                 pr_err("%s: no space in alarm id table\n", __func__);
306                 return;
307         }
308
309         alarm_info = &th_ctxt.alarm_table[th_ctxt.alarmid_cnt++];
310
311         alarm_info->alarmid = (void*)(&alarm->node.node);
312         alarm_info->caller  = caller;
313         alarm_info->handler = alarm->function;
314         set_alarm_owner(alarm_info, caller);
315
316         return;
317 }
318
319 void __time_history_alarm_start(const struct alarm *alarm, void *caller)
320 {
321         time_history_insert_alarm_log( TIME_HISTORY_TYPE_ALARM_START, alarm, caller);
322 }
323
324 void __time_history_alarm_restart(const struct alarm *alarm, void *caller)
325 {
326         time_history_insert_alarm_log(TIME_HISTORY_TYPE_ALARM_RESTART, alarm, caller);
327 }
328
329 void __time_history_alarm_expired(const struct alarm *alarm, ktime_t now)
330 {
331         time_history_insert_alarm_log(TIME_HISTORY_TYPE_ALARM_EXPIRED,
332                         alarm, __builtin_return_address(0));
333 }
334
335 void __time_history_alarm_del(const struct alarm *alarm, void *caller)
336 {
337         if (time_history_get_alarm_by_id(&alarm->node.node) == NULL)
338                 return;
339
340         time_history_insert_alarm_log(TIME_HISTORY_TYPE_ALARM_DEL, alarm, caller);
341 }
342
343 void __time_history_time_set(const struct timespec *oldtime,
344                 const struct timespec *newtime, void *caller)
345 {
346         time_history_insert_time_log(TIME_HISTORY_TYPE_TIME_SET,
347                         oldtime, newtime, caller, 0);
348 }
349
350 void __time_history_rtc_time_set(const struct timespec *newtime,
351                 void *caller, int err)
352 {
353         time_history_insert_time_log(TIME_HISTORY_TYPE_RTC_TIME_SET,
354                         newtime, newtime, caller, err);
355 }
356
357 void __time_history_rtc_alarm_init(const struct rtc_timer *timer,
358                 void *caller)
359 {
360         struct alarm_info *alarm_info;
361
362         if (th_ctxt.alarmid_cnt >= ALARM_ID_MAX) {
363                 pr_err("%s: no space in alarm id table\n", __func__);
364                 return;
365         }
366
367         alarm_info = &th_ctxt.alarm_table[th_ctxt.alarmid_cnt++];
368
369         alarm_info->alarmid = (void*)(&timer->node.node);
370         alarm_info->caller  = caller;
371         alarm_info->handler = timer->task.func;
372         set_alarm_owner(alarm_info, caller);
373
374         return;
375 }
376
377 void __time_history_rtc_alarm_set(struct rtc_device *rtc,
378                 const struct rtc_wkalrm *wkalrm, void *caller, int err)
379 {
380         struct timerqueue_node *tq_node;
381         struct alarm_info *alarm;
382         struct time_log_entry *entry;
383         unsigned long flags;
384
385         tq_node = timerqueue_getnext(&rtc->timerqueue);
386         if (!tq_node) {
387                 pr_err("%s: timerqueue is empty\n", __func__);
388                 return;
389         }
390
391         alarm = time_history_get_alarm_by_id(&tq_node->node);
392         if (!alarm) {
393                 pr_err("%s: can't find alarm\n", __func__);
394                 return;
395         }
396
397 #ifdef CONFIG_TIME_HISTORY_LOG_FILTER
398         if (time_history_check_log_filter(&tq_node->node) != 0)
399                 return;
400 #endif
401
402         spin_lock_irqsave(&th_ctxt.lock, flags);
403         entry = time_history_init_log_entry(&alarm_log);
404         entry->history_type = TIME_HISTORY_TYPE_RTC_ALARM_SET;
405         entry->newtime = ktime_to_timespec(tq_node->expires);
406         entry->caller  = caller;
407         entry->alarmid = (void*)&tq_node->node;
408         entry->pid = current->pid;
409         entry->err = err;
410         memcpy(entry->comm, current->comm, TASK_COMM_LEN);
411
412         th_ctxt.history_cnt++;
413         spin_unlock_irqrestore(&th_ctxt.lock, flags);
414
415         return;
416 }
417
418 static const char *history_type_name[] = {
419         [TIME_HISTORY_TYPE_TIME_SET]          = "time_set",
420         [TIME_HISTORY_TYPE_RTC_TIME_SET]      = "rtc_time_set",
421         [TIME_HISTORY_TYPE_HOST_TIME_SET]     = "host_time",
422         [TIME_HISTORY_TYPE_NETWORK_TIME_SET]  = "network_time",
423         [TIME_HISTORY_TYPE_TIMEZONE_SET]      = "timezone_set",
424         [TIME_HISTORY_TYPE_NITZ_UPDATE_SET]   = "nitz_update",
425         [TIME_HISTORY_TYPE_ALARM_START]       = "alarm_start",
426         [TIME_HISTORY_TYPE_ALARM_RESTART]     = "alarm_restart",
427         [TIME_HISTORY_TYPE_ALARM_EXPIRED]     = "alarm_expired",
428         [TIME_HISTORY_TYPE_ALARM_DEL]         = "alarm_delete",
429         [TIME_HISTORY_TYPE_RTC_ALARM_SET]     = "rtc_alarm_set",
430 };
431
432 struct time_history_iter {
433         struct time_history_log_buf *curr_log;
434         int curr_idx;
435         struct time_history_log_buf *comp_log;
436         int comp_idx;
437 };
438
439 static char owner_filter[32] = "-";
440
441 static inline void seq_print_difftime(struct seq_file *seq,
442                 struct timespec old, struct timespec new)
443 {
444         struct timespec diff = timespec_sub(new, old);
445
446         if (diff.tv_sec < 0 && diff.tv_nsec > 0) {
447                 /* It's normalized time, convert to human readable time format */
448                 diff.tv_nsec = -(diff.tv_nsec - NSEC_PER_SEC);
449                 diff.tv_sec++;
450         }
451
452         seq_printf(seq, " (%s%ld.%03ld)", (diff.tv_sec >= 0)? "+" : "",
453                         diff.tv_sec, diff.tv_nsec/NSEC_PER_MSEC);
454         return;
455 }
456
457 static inline void seq_print_realtime(struct seq_file *seq, struct timespec *ts,
458                 time_t tz_offset)
459 {
460         struct tm tm;
461
462         time_to_tm(ts->tv_sec + tz_offset, 0, &tm);
463
464         seq_printf(seq, "%4ld-%02d-%02d %02d:%02d:%02d",
465                         tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
466                         tm.tm_hour, tm.tm_min, tm.tm_sec);
467 }
468
469 static void time_history_show_header(struct seq_file *seq)
470 {
471         struct timespec ts;
472         struct rtc_device *rtc_dev;
473         char *tz, *tz_id, *nitz;
474         time_t tz_offset = time_history_timezone_offset();
475
476         tz    = th_ctxt.timezone;
477         tz_id = th_ctxt.timezone_id;
478
479         switch (th_ctxt.nitz_update) {
480                 case 0:
481                         nitz = "off";
482                         break;
483                 case 1:
484                         nitz = "on";
485                         break;
486                 default:
487                         nitz = "Unknown";
488         }
489
490         getnstimeofday(&ts);
491         seq_puts(seq, "\n********** time_history v0.2 **********\n\n");
492         seq_puts(seq, "system time : ");
493         seq_print_realtime(seq, &ts, tz_offset);
494         seq_printf(seq, " (%ld.%03ld)\n", ts.tv_sec, ts.tv_nsec/NSEC_PER_MSEC);
495         rtc_dev = rtc_class_open("rtc0");
496         if (rtc_dev) {
497                 struct rtc_time tm;
498                 unsigned long time;
499                 rtc_read_time(rtc_dev, &tm);
500                 rtc_tm_to_time(&tm, &time);
501                 seq_printf(seq, "rtc time    : %d-%02d-%02d %02d:%02d:%02d (%ld)\n",
502                                 tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
503                                 tm.tm_hour, tm.tm_min, tm.tm_sec, time);
504                 rtc_class_close(rtc_dev);
505         }
506         seq_printf(seq, "timezone    : %s (%s)\n", tz_id, tz);
507         seq_printf(seq, "nitz update : %s\n", nitz);
508
509         seq_printf(seq, "total alarmtimer : %4d/%-d\n",
510                         th_ctxt.alarmid_cnt, ARRAY_SIZE(th_ctxt.alarm_table));
511         seq_printf(seq, "total time set   : %4lld/%-d\n",
512                         time_log.cnt, time_log.buf_size);
513         seq_printf(seq, "total alarm log  : %4lld/%-d\n\n",
514                         alarm_log.cnt, alarm_log.buf_size);
515
516 #ifdef CONFIG_TIME_HISTORY_DEBUG
517         {
518                 struct alarm_info *alarm;
519                 seq_printf(seq, "log_entry sz: %d\n\n", sizeof(struct time_log_entry));
520                 for_each_alarm(th_ctxt.alarm_table, alarm) {
521                         seq_printf(seq, "id      : %p\n", alarm->alarmid);
522                         seq_printf(seq, "owner   : %s\n", alarm->owner);
523                         seq_printf(seq, "handler : %pf\n", alarm->handler);
524                         seq_printf(seq, "caller  : %pf\n\n", alarm->caller);
525                 }
526         }
527 #endif
528         seq_puts(seq, "      real_time       tz   [ kernel_time ");
529 #ifdef CONFIG_TIME_HISTORY_SAVE_BOOTTIME
530         seq_puts(seq, " / boot_time");
531 #endif
532         seq_puts(seq, "]  idx  type           worker            "
533                         "function                time                  epoch"
534                         "       diff\n");
535 }
536
537 static inline void seq_print_timestamp(struct seq_file *seq,
538                 struct time_log_entry *entry)
539 {
540         char tz[8] = "+00:00";
541
542         if (entry->tz_offset) {
543                 int tz_hour, tz_min;
544                 time_t tz_offset;
545                 tz_offset = (entry->tz_offset < 0)? -(entry->tz_offset) : entry->tz_offset;
546                 tz_hour = tz_offset/3600;
547                 tz_min  = (tz_offset%3600)/60;
548                 snprintf(tz, sizeof(tz) - 1, "%c%02d:%02d",
549                                 (entry->tz_offset < 0)? '-' : '+', tz_hour, tz_min);
550         }
551
552         seq_print_realtime(seq, &entry->realtime, entry->tz_offset);
553         seq_printf(seq, " %s [%6lu.%06lu", tz,
554                         entry->monotonic.tv_sec, entry->monotonic.tv_nsec/NSEC_PER_USEC);
555 #ifdef CONFIG_TIME_HISTORY_SAVE_BOOTTIME
556         seq_printf(seq, " /  %5lu.%03lu",
557                         entry->boottime.tv_sec, entry->boottime.tv_nsec/NSEC_PER_MSEC);
558 #endif
559         seq_puts(seq, "] ");
560 }
561
562 static int time_history_show(struct seq_file *seq, void *v)
563 {
564         struct time_history_iter *iter = seq->private;
565         struct time_log_entry *entry;
566         struct alarm_info *alarm = NULL;
567         enum {
568                 TIME_LOG,
569                 ALARM_LOG,
570                 ETC_LOG,
571         } log_type;
572
573         entry = &iter->curr_log->buf[iter->curr_idx];
574
575         switch (entry->history_type) {
576                 case TIME_HISTORY_TYPE_TIME_SET ... TIME_HISTORY_TYPE_NETWORK_TIME_SET:
577                         log_type = TIME_LOG;
578                         break;
579                 case TIME_HISTORY_TYPE_ALARM_START ... TIME_HISTORY_TYPE_ALARM_DEL:
580                 case TIME_HISTORY_TYPE_RTC_ALARM_SET:
581                         log_type = ALARM_LOG;
582                         break;
583                 case TIME_HISTORY_TYPE_TIMEZONE_SET:
584                 case TIME_HISTORY_TYPE_NITZ_UPDATE_SET:
585                         log_type = ETC_LOG;
586                         break;
587                 default:
588                         return 0;
589         };
590
591         if (log_type == ALARM_LOG) {
592                 alarm = time_history_get_alarm_by_id(entry->alarmid);
593
594                 if ((owner_filter[0] != '-' && alarm
595                         && strcasecmp(alarm->owner, owner_filter) != 0))
596                 return 0;
597         }
598
599         seq_print_timestamp(seq, entry);
600         seq_printf(seq, "%4lld  %-13s  ", entry->history_idx,
601                         history_type_name[entry->history_type]);
602
603         if ((entry->history_type == TIME_HISTORY_TYPE_ALARM_EXPIRED ||
604                         entry->history_type == TIME_HISTORY_TYPE_RTC_ALARM_SET) && alarm) {
605                 seq_printf(seq, "%-16s  ", alarm->owner);
606         } else if (entry->history_type == TIME_HISTORY_TYPE_NITZ_UPDATE_SET) {
607                 if (entry->oldtime.tv_sec == -1)
608                         seq_printf(seq, "init: %ld", entry->newtime.tv_sec);
609                 else
610                         seq_printf(seq, "%ld > %ld", entry->oldtime.tv_sec, entry->newtime.tv_sec);
611         } else {
612                 seq_printf(seq, "%-16s  ", entry->comm);
613         }
614
615         if (log_type == ETC_LOG) {
616                 seq_puts(seq, "\n");
617                 return 0;
618         }
619
620         if (entry->history_type == TIME_HISTORY_TYPE_ALARM_EXPIRED && alarm)
621                 seq_printf(seq, "%-22pf  ", alarm->handler);
622         else if (entry->caller)
623                 seq_printf(seq, "%-22pf  ", entry->caller);
624         else
625                 seq_printf(seq, "%24s", "");
626
627         if (log_type == TIME_LOG || is_realtime(&entry->newtime)) {
628                 seq_print_realtime(seq, &entry->newtime, entry->tz_offset);
629                 seq_printf(seq, "   %10ld ", entry->newtime.tv_sec);
630         } else {
631                 seq_printf(seq, "%15lu.%03lu",
632                                 entry->newtime.tv_sec, entry->newtime.tv_nsec/NSEC_PER_MSEC);
633         }
634
635         if (log_type == TIME_LOG)
636                 seq_print_difftime(seq, entry->realtime, entry->newtime);
637
638         if (entry->err)
639                 seq_printf(seq, " -> err: %d", entry->err);
640
641         seq_puts(seq, "\n");
642         return 0;
643 }
644
645 static struct time_history_iter *swap_log(struct time_history_iter *iter)
646 {
647         struct time_history_log_buf *temp_log = iter->curr_log;
648         int temp_idx = iter->curr_idx;
649
650         if (unlikely(!iter))
651                 return NULL;
652
653         /* Keep current log buf */
654         if (!iter->comp_log)
655                 return (iter->curr_log)? iter : NULL;
656
657         iter->curr_log = iter->comp_log;
658         iter->comp_log = temp_log;
659
660         iter->curr_idx = iter->comp_idx;
661         iter->comp_idx = temp_idx;
662
663         return iter;
664 }
665
666 static void *move_iter(struct time_history_iter *iter)
667 {
668         if (!iter->curr_log)
669                 return NULL;
670
671         if (iter->curr_idx == iter->curr_log->tail) {
672                 iter->curr_log = NULL;
673                 return swap_log(iter);
674         } else if (iter->curr_idx + 1 >= iter->curr_log->buf_size) {
675                 iter->curr_idx = 0;
676         } else {
677                 iter->curr_idx++;
678         }
679
680         if (iter->comp_log && (iter->curr_log->buf[iter->curr_idx].history_idx
681                         > iter->comp_log->buf[iter->comp_idx].history_idx)) {
682                 return swap_log(iter);
683         }
684
685         return iter;
686 }
687
688 static void *time_history_start(struct seq_file *seq, loff_t *pos)
689 {
690         struct time_history_iter *iter = seq->private;
691
692         if (*pos >= th_ctxt.history_cnt)
693                 return NULL;
694
695         if (*pos)
696                 return move_iter(iter);
697
698         time_history_show_header(seq);
699
700         if (time_log.buf[time_log.head].history_idx
701                         < alarm_log.buf[alarm_log.head].history_idx) {
702                 iter->curr_log = &time_log;
703                 iter->curr_idx = time_log.head;
704                 iter->comp_log = &alarm_log;
705                 iter->comp_idx = alarm_log.head;
706         } else {
707                 iter->curr_log = &alarm_log;
708                 iter->curr_idx = alarm_log.head;
709                 iter->comp_log = &time_log;
710                 iter->comp_idx = time_log.head;
711         }
712
713         return iter;
714 }
715
716 static void *time_history_next(struct seq_file *seq, void *v, loff_t *pos)
717 {
718         struct time_history_iter *iter = seq->private;
719
720         (*pos)++;
721
722         if (*pos >= th_ctxt.history_cnt)
723                 return NULL;
724
725         return move_iter(iter);
726 }
727
728 static void time_history_stop(struct seq_file *seq, void *v)
729 {
730 }
731
732 static const struct seq_operations time_history_seq_ops = {
733         .start = time_history_start,
734         .show  = time_history_show,
735         .next  = time_history_next,
736         .stop  = time_history_stop,
737 };
738
739 static int time_history_open(struct inode *inode, struct file *file)
740 {
741         return seq_open_private(file, &time_history_seq_ops,
742                         sizeof(struct time_history_iter));
743 }
744
745 /* return true if valid timezone format */
746 static int time_history_valid_tz_format(char *timezone)
747 {
748         int tz_hour, tz_min;
749         int len;
750
751         if (unlikely(!timezone))
752                 return false;
753
754         len = strlen(timezone);
755         if (len < 5 || len > 6)
756                 return false;
757
758         if (timezone[0] != '+' && timezone[0] != '-')
759                 return false;
760
761         if (sscanf(&timezone[1], "%d:%d", &tz_hour, &tz_min) < 0)
762                 return false;
763
764         if (tz_hour > 14)
765                 return false;
766
767         if (tz_min != 0 && tz_min != 30 && tz_min != 45)
768                 return false;
769
770         return true;
771 }
772
773 static ssize_t time_history_write(struct file *file,
774                 const char __user *buf, size_t count, loff_t *pos)
775 {
776         char option[32] = {0};
777         char val[64] = {0};
778
779         if (sscanf(buf, "%s %s", option, val) < 0)
780                 return -EINVAL;
781
782         if (strcasecmp(option, "timezone") == 0) {
783                 if (!time_history_valid_tz_format(val)) {
784                         pr_err("%s: Invalid timezone format: %s\n", __func__, val);
785                         return -EINVAL;
786                 }
787                 time_history_insert_timezone_log(th_ctxt.timezone, val);
788                 memset(th_ctxt.timezone, 0, sizeof(th_ctxt.timezone));
789                 snprintf(th_ctxt.timezone, sizeof(th_ctxt.timezone) - 1, "%s", val);
790         } else if (strcasecmp(option, "timezone_id") == 0) {
791                 memset(th_ctxt.timezone_id, 0, sizeof(th_ctxt.timezone_id));
792                 snprintf(th_ctxt.timezone_id, sizeof(th_ctxt.timezone_id) - 1, "%s", val);
793         } else if (strcasecmp(option, "host") == 0) {
794                 struct timespec newtime = {0, 0};
795                 sscanf(val, "%ld", &newtime.tv_sec);
796                 time_history_insert_time_log(TIME_HISTORY_TYPE_HOST_TIME_SET,
797                                 &newtime, &newtime, NULL, 0);
798         } else if (strcasecmp(option, "nitz") == 0) {
799                 struct timespec newtime = {0, 0};
800                 sscanf(val, "%ld", &newtime.tv_sec);
801                 time_history_insert_time_log(TIME_HISTORY_TYPE_NETWORK_TIME_SET,
802                                 &newtime, &newtime, NULL, 0);
803         } else if (strcasecmp(option, "nitz_update") == 0) {
804                 int nitz_update = -1;
805                 sscanf(val, "%d", &nitz_update);
806                 time_history_insert_setting_log(TIME_HISTORY_TYPE_NITZ_UPDATE_SET,
807                                 th_ctxt.nitz_update, nitz_update);
808                 th_ctxt.nitz_update = nitz_update;
809         } else if (strcasecmp(option, "filter") == 0) {
810                 char *cur;
811                 memset(owner_filter, 0, sizeof(owner_filter));
812                 snprintf(owner_filter, sizeof(owner_filter), "%s", val);
813                 memset(th_ctxt.timezone, 0, sizeof(th_ctxt.timezone));
814                 cur = strchr(owner_filter, '\n');
815                 if (cur)
816                         *cur = '\0';
817         } else {
818                 pr_info("%s: Failed to parse option\n", __func__);
819                 return -EINVAL;
820         }
821
822         return count;
823 }
824
825 static const struct file_operations time_history_fops = {
826         .open           = time_history_open,
827         .read           = seq_read,
828         .write          = time_history_write,
829         .llseek         = seq_lseek,
830         .release        = seq_release_private,
831 };
832
833 static int __init time_history_init(void)
834 {
835         struct proc_dir_entry *pe;
836
837         pe = proc_create("time_history", 0444, NULL, &time_history_fops);
838         if (!pe) {
839                 return -ENOMEM;
840         }
841
842         spin_lock_init(&th_ctxt.lock);
843
844         time_log.buf[0].history_idx  = ULLONG_MAX;
845         alarm_log.buf[0].history_idx = ULLONG_MAX;
846
847         return 0;
848 }
849 arch_initcall(time_history_init);