Commit | Line | Data |
---|---|---|
038200cf CH |
1 | /* |
2 | * Copyright (C) 2007 IBM Deutschland Entwicklung GmbH | |
3 | * Released under GPL v2. | |
4 | * | |
5 | * Partially based on net/ipv4/tcp_probe.c. | |
6 | * | |
7 | * Simple tracing facility for spu contexts. | |
8 | */ | |
9 | #include <linux/sched.h> | |
10 | #include <linux/kernel.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/marker.h> | |
13 | #include <linux/proc_fs.h> | |
14 | #include <linux/wait.h> | |
15 | #include <asm/atomic.h> | |
16 | #include <asm/uaccess.h> | |
17 | #include "spufs.h" | |
18 | ||
19 | struct spu_probe { | |
20 | const char *name; | |
21 | const char *format; | |
22 | marker_probe_func *probe_func; | |
23 | }; | |
24 | ||
25 | struct sputrace { | |
26 | ktime_t tstamp; | |
27 | int owner_tid; /* owner */ | |
28 | int curr_tid; | |
29 | const char *name; | |
30 | int number; | |
31 | }; | |
32 | ||
33 | static int bufsize __read_mostly = 16384; | |
34 | MODULE_PARM_DESC(bufsize, "Log buffer size (number of records)"); | |
35 | module_param(bufsize, int, 0); | |
36 | ||
37 | ||
38 | static DEFINE_SPINLOCK(sputrace_lock); | |
39 | static DECLARE_WAIT_QUEUE_HEAD(sputrace_wait); | |
40 | static ktime_t sputrace_start; | |
41 | static unsigned long sputrace_head, sputrace_tail; | |
42 | static struct sputrace *sputrace_log; | |
baf39927 | 43 | static int sputrace_logging; |
038200cf CH |
44 | |
45 | static int sputrace_used(void) | |
46 | { | |
47 | return (sputrace_head - sputrace_tail) % bufsize; | |
48 | } | |
49 | ||
50 | static inline int sputrace_avail(void) | |
51 | { | |
52 | return bufsize - sputrace_used(); | |
53 | } | |
54 | ||
55 | static int sputrace_sprint(char *tbuf, int n) | |
56 | { | |
57 | const struct sputrace *t = sputrace_log + sputrace_tail % bufsize; | |
58 | struct timespec tv = | |
59 | ktime_to_timespec(ktime_sub(t->tstamp, sputrace_start)); | |
60 | ||
61 | return snprintf(tbuf, n, | |
71791bee | 62 | "[%lu.%09lu] %d: %s (ctxthread = %d, spu = %d)\n", |
038200cf CH |
63 | (unsigned long) tv.tv_sec, |
64 | (unsigned long) tv.tv_nsec, | |
038200cf | 65 | t->curr_tid, |
71791bee JK |
66 | t->name, |
67 | t->owner_tid, | |
038200cf CH |
68 | t->number); |
69 | } | |
70 | ||
71 | static ssize_t sputrace_read(struct file *file, char __user *buf, | |
72 | size_t len, loff_t *ppos) | |
73 | { | |
74 | int error = 0, cnt = 0; | |
75 | ||
76 | if (!buf || len < 0) | |
77 | return -EINVAL; | |
78 | ||
79 | while (cnt < len) { | |
80 | char tbuf[128]; | |
81 | int width; | |
82 | ||
e869446b JK |
83 | /* If we have data ready to return, don't block waiting |
84 | * for more */ | |
85 | if (cnt > 0 && sputrace_used() == 0) | |
86 | break; | |
87 | ||
038200cf CH |
88 | error = wait_event_interruptible(sputrace_wait, |
89 | sputrace_used() > 0); | |
90 | if (error) | |
91 | break; | |
92 | ||
93 | spin_lock(&sputrace_lock); | |
94 | if (sputrace_head == sputrace_tail) { | |
95 | spin_unlock(&sputrace_lock); | |
96 | continue; | |
97 | } | |
98 | ||
99 | width = sputrace_sprint(tbuf, sizeof(tbuf)); | |
100 | if (width < len) | |
101 | sputrace_tail = (sputrace_tail + 1) % bufsize; | |
102 | spin_unlock(&sputrace_lock); | |
103 | ||
104 | if (width >= len) | |
105 | break; | |
106 | ||
107 | error = copy_to_user(buf + cnt, tbuf, width); | |
108 | if (error) | |
109 | break; | |
110 | cnt += width; | |
111 | } | |
112 | ||
113 | return cnt == 0 ? error : cnt; | |
114 | } | |
115 | ||
116 | static int sputrace_open(struct inode *inode, struct file *file) | |
117 | { | |
baf39927 JK |
118 | int rc; |
119 | ||
038200cf | 120 | spin_lock(&sputrace_lock); |
baf39927 JK |
121 | if (sputrace_logging) { |
122 | rc = -EBUSY; | |
123 | goto out; | |
124 | } | |
125 | ||
126 | sputrace_logging = 1; | |
038200cf CH |
127 | sputrace_head = sputrace_tail = 0; |
128 | sputrace_start = ktime_get(); | |
baf39927 JK |
129 | rc = 0; |
130 | ||
131 | out: | |
038200cf | 132 | spin_unlock(&sputrace_lock); |
baf39927 JK |
133 | return rc; |
134 | } | |
038200cf | 135 | |
baf39927 JK |
136 | static int sputrace_release(struct inode *inode, struct file *file) |
137 | { | |
138 | spin_lock(&sputrace_lock); | |
139 | sputrace_logging = 0; | |
140 | spin_unlock(&sputrace_lock); | |
038200cf CH |
141 | return 0; |
142 | } | |
143 | ||
144 | static const struct file_operations sputrace_fops = { | |
baf39927 JK |
145 | .owner = THIS_MODULE, |
146 | .open = sputrace_open, | |
147 | .read = sputrace_read, | |
148 | .release = sputrace_release, | |
038200cf CH |
149 | }; |
150 | ||
151 | static void sputrace_log_item(const char *name, struct spu_context *ctx, | |
152 | struct spu *spu) | |
153 | { | |
154 | spin_lock(&sputrace_lock); | |
baf39927 JK |
155 | |
156 | if (!sputrace_logging) { | |
157 | spin_unlock(&sputrace_lock); | |
158 | return; | |
159 | } | |
160 | ||
038200cf CH |
161 | if (sputrace_avail() > 1) { |
162 | struct sputrace *t = sputrace_log + sputrace_head; | |
163 | ||
164 | t->tstamp = ktime_get(); | |
165 | t->owner_tid = ctx->tid; | |
166 | t->name = name; | |
167 | t->curr_tid = current->pid; | |
168 | t->number = spu ? spu->number : -1; | |
169 | ||
170 | sputrace_head = (sputrace_head + 1) % bufsize; | |
171 | } else { | |
172 | printk(KERN_WARNING | |
173 | "sputrace: lost samples due to full buffer.\n"); | |
174 | } | |
175 | spin_unlock(&sputrace_lock); | |
176 | ||
177 | wake_up(&sputrace_wait); | |
178 | } | |
179 | ||
fb40bd78 MD |
180 | static void spu_context_event(void *probe_private, void *call_data, |
181 | const char *format, va_list *args) | |
038200cf | 182 | { |
fb40bd78 | 183 | struct spu_probe *p = probe_private; |
038200cf CH |
184 | struct spu_context *ctx; |
185 | struct spu *spu; | |
186 | ||
fb40bd78 MD |
187 | ctx = va_arg(*args, struct spu_context *); |
188 | spu = va_arg(*args, struct spu *); | |
038200cf CH |
189 | |
190 | sputrace_log_item(p->name, ctx, spu); | |
038200cf CH |
191 | } |
192 | ||
fb40bd78 MD |
193 | static void spu_context_nospu_event(void *probe_private, void *call_data, |
194 | const char *format, va_list *args) | |
038200cf | 195 | { |
fb40bd78 | 196 | struct spu_probe *p = probe_private; |
038200cf CH |
197 | struct spu_context *ctx; |
198 | ||
fb40bd78 | 199 | ctx = va_arg(*args, struct spu_context *); |
038200cf CH |
200 | |
201 | sputrace_log_item(p->name, ctx, NULL); | |
038200cf CH |
202 | } |
203 | ||
204 | struct spu_probe spu_probes[] = { | |
d6508aaf JMV |
205 | { "spu_bind_context__enter", "ctx %p spu %p", spu_context_event }, |
206 | { "spu_unbind_context__enter", "ctx %p spu %p", spu_context_event }, | |
207 | { "spu_get_idle__enter", "ctx %p", spu_context_nospu_event }, | |
208 | { "spu_get_idle__found", "ctx %p spu %p", spu_context_event }, | |
209 | { "spu_get_idle__not_found", "ctx %p", spu_context_nospu_event }, | |
210 | { "spu_find_victim__enter", "ctx %p", spu_context_nospu_event }, | |
211 | { "spusched_tick__preempt", "ctx %p spu %p", spu_context_event }, | |
212 | { "spusched_tick__newslice", "ctx %p", spu_context_nospu_event }, | |
213 | { "spu_yield__enter", "ctx %p", spu_context_nospu_event }, | |
214 | { "spu_deactivate__enter", "ctx %p", spu_context_nospu_event }, | |
215 | { "__spu_deactivate__unload", "ctx %p spu %p", spu_context_event }, | |
b1e2270f NP |
216 | { "spufs_ps_fault__enter", "ctx %p", spu_context_nospu_event }, |
217 | { "spufs_ps_fault__sleep", "ctx %p", spu_context_nospu_event }, | |
218 | { "spufs_ps_fault__wake", "ctx %p spu %p", spu_context_event }, | |
219 | { "spufs_ps_fault__insert", "ctx %p spu %p", spu_context_event }, | |
d6508aaf JMV |
220 | { "spu_acquire_saved__enter", "ctx %p", spu_context_nospu_event }, |
221 | { "destroy_spu_context__enter", "ctx %p", spu_context_nospu_event }, | |
222 | { "spufs_stop_callback__enter", "ctx %p spu %p", spu_context_event }, | |
038200cf CH |
223 | }; |
224 | ||
225 | static int __init sputrace_init(void) | |
226 | { | |
227 | struct proc_dir_entry *entry; | |
228 | int i, error = -ENOMEM; | |
229 | ||
9bcab840 | 230 | sputrace_log = kcalloc(bufsize, sizeof(struct sputrace), GFP_KERNEL); |
038200cf CH |
231 | if (!sputrace_log) |
232 | goto out; | |
233 | ||
66747138 | 234 | entry = proc_create("sputrace", S_IRUSR, NULL, &sputrace_fops); |
038200cf CH |
235 | if (!entry) |
236 | goto out_free_log; | |
038200cf CH |
237 | |
238 | for (i = 0; i < ARRAY_SIZE(spu_probes); i++) { | |
239 | struct spu_probe *p = &spu_probes[i]; | |
240 | ||
241 | error = marker_probe_register(p->name, p->format, | |
242 | p->probe_func, p); | |
243 | if (error) | |
244 | printk(KERN_INFO "Unable to register probe %s\n", | |
245 | p->name); | |
038200cf CH |
246 | } |
247 | ||
248 | return 0; | |
249 | ||
250 | out_free_log: | |
251 | kfree(sputrace_log); | |
252 | out: | |
253 | return -ENOMEM; | |
254 | } | |
255 | ||
256 | static void __exit sputrace_exit(void) | |
257 | { | |
258 | int i; | |
259 | ||
260 | for (i = 0; i < ARRAY_SIZE(spu_probes); i++) | |
fb40bd78 MD |
261 | marker_probe_unregister(spu_probes[i].name, |
262 | spu_probes[i].probe_func, &spu_probes[i]); | |
038200cf CH |
263 | |
264 | remove_proc_entry("sputrace", NULL); | |
265 | kfree(sputrace_log); | |
266 | } | |
267 | ||
268 | module_init(sputrace_init); | |
269 | module_exit(sputrace_exit); | |
270 | ||
271 | MODULE_LICENSE("GPL"); |