2 * timer.c - timer library support for ktap
4 * This file is part of ktap by Jovi Zhangwei.
6 * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
8 * ktap is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU General Public License,
10 * version 2, as published by the Free Software Foundation.
12 * ktap is distributed in the hope it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <linux/ctype.h>
23 #include <linux/slab.h>
24 #include <linux/delay.h>
25 #include <linux/sched.h>
26 #include "../include/ktap_types.h"
36 struct list_head list;
40 * Currently ktap disallow tracing event in timer callback closure,
41 * that will corrupt ktap_state and ktap stack, because timer closure
42 * and event closure use same irq percpu ktap_state and stack.
43 * We can use a different percpu ktap_state and stack for timer purpuse,
44 * but that's don't bring any big value with cost on memory consuming.
46 * So just simply disable tracing in timer closure,
47 * get_recursion_context()/put_recursion_context() is used for this purpose.
49 static enum hrtimer_restart hrtimer_ktap_fn(struct hrtimer *timer)
51 struct hrtimer_ktap *t;
55 rcu_read_lock_sched_notrace();
57 t = container_of(timer, struct hrtimer_ktap, timer);
58 rctx = get_recursion_context(t->ks);
60 ks = kp_newthread(t->ks);
61 set_closure(ks->top, t->cl);
63 kp_call(ks, ks->top - 1, 0);
66 hrtimer_add_expires_ns(timer, t->ns);
68 put_recursion_context(ks, rctx);
69 rcu_read_unlock_sched_notrace();
71 return HRTIMER_RESTART;
74 static void set_tick_timer(ktap_state *ks, u64 period, ktap_closure *cl)
76 struct hrtimer_ktap *t;
78 t = kp_malloc(ks, sizeof(*t));
83 INIT_LIST_HEAD(&t->list);
84 list_add(&t->list, &(G(ks)->timers));
86 hrtimer_init(&t->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
87 t->timer.function = hrtimer_ktap_fn;
88 hrtimer_start(&t->timer, ns_to_ktime(period), HRTIMER_MODE_REL);
91 static void set_profile_timer(ktap_state *ks, u64 period, ktap_closure *cl)
93 struct perf_event_attr attr;
95 memset(&attr, 0, sizeof(attr));
96 attr.type = PERF_TYPE_SOFTWARE;
97 attr.config = PERF_COUNT_SW_CPU_CLOCK;
98 attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
99 PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD;
100 attr.sample_period = period;
101 attr.size = sizeof(attr);
104 kp_perf_event_register(ks, &attr, NULL, NULL, cl);
107 static int do_tick_profile(ktap_state *ks, int is_tick)
109 const char *str, *tmp;
110 char interval_str[32] = {0};
111 char suffix[10] = {0};
115 kp_arg_check(ks, 1, KTAP_TSTRING);
116 kp_arg_check(ks, 2, KTAP_TFUNCTION);
118 str = svalue(kp_arg(ks, 1));
120 while (isdigit(*tmp))
123 strncpy(interval_str, str, tmp - str);
124 if (kstrtoint(interval_str, 10, &n))
127 strncpy(suffix, tmp, 9);
128 while (suffix[i] != ' ' && suffix[i] != '\0')
133 if (!strcmp(suffix, "s") || !strcmp(suffix, "sec"))
134 factor = NSEC_PER_SEC;
135 else if (!strcmp(suffix, "ms") || !strcmp(suffix, "msec"))
136 factor = NSEC_PER_MSEC;
137 else if (!strcmp(suffix, "us") || !strcmp(suffix, "usec"))
138 factor = NSEC_PER_USEC;
143 set_tick_timer(ks, (u64)factor * n, clvalue(kp_arg(ks, 2)));
145 set_profile_timer(ks, (u64)factor * n, clvalue(kp_arg(ks, 2)));
150 kp_error(ks, "cannot parse timer interval: %s\n", str);
155 * tick-n probes fire on only one CPU per interval.
156 * valid time suffixes: sec/s, msec/ms, usec/us
158 static int ktap_lib_tick(ktap_state *ks)
160 return do_tick_profile(ks, 1);
164 * A profile-n probe fires every fixed interval on every CPU
165 * valid time suffixes: sec/s, msec/ms, usec/us
167 static int ktap_lib_profile(ktap_state *ks)
169 return do_tick_profile(ks, 0);
172 void kp_exit_timers(ktap_state *ks)
174 struct hrtimer_ktap *t, *tmp;
175 struct list_head *timers_list = &(G(ks)->timers);
177 list_for_each_entry_safe(t, tmp, timers_list, list) {
178 hrtimer_cancel(&t->timer);
183 static const ktap_Reg timerlib_funcs[] = {
184 {"profile", ktap_lib_profile},
185 {"tick", ktap_lib_tick},
189 void kp_init_timerlib(ktap_state *ks)
191 kp_register_lib(ks, "timer", timerlib_funcs);