2 * Copyright (C) 2007 IBM Deutschland Entwicklung GmbH
3 * Released under GPL v2.
5 * Partially based on net/ipv4/tcp_probe.c.
7 * Simple tracing facility for spu contexts.
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>
22 marker_probe_func
*probe_func
;
27 int owner_tid
; /* owner */
33 static int bufsize __read_mostly
= 16384;
34 MODULE_PARM_DESC(bufsize
, "Log buffer size (number of records)");
35 module_param(bufsize
, int, 0);
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
;
43 static int sputrace_logging
;
45 static int sputrace_used(void)
47 return (sputrace_head
- sputrace_tail
) % bufsize
;
50 static inline int sputrace_avail(void)
52 return bufsize
- sputrace_used();
55 static int sputrace_sprint(char *tbuf
, int n
)
57 const struct sputrace
*t
= sputrace_log
+ sputrace_tail
% bufsize
;
59 ktime_to_timespec(ktime_sub(t
->tstamp
, sputrace_start
));
61 return snprintf(tbuf
, n
,
62 "[%lu.%09lu] %d: %s (ctxthread = %d, spu = %d)\n",
63 (unsigned long) tv
.tv_sec
,
64 (unsigned long) tv
.tv_nsec
,
71 static ssize_t
sputrace_read(struct file
*file
, char __user
*buf
,
72 size_t len
, loff_t
*ppos
)
74 int error
= 0, cnt
= 0;
83 /* If we have data ready to return, don't block waiting
85 if (cnt
> 0 && sputrace_used() == 0)
88 error
= wait_event_interruptible(sputrace_wait
,
93 spin_lock(&sputrace_lock
);
94 if (sputrace_head
== sputrace_tail
) {
95 spin_unlock(&sputrace_lock
);
99 width
= sputrace_sprint(tbuf
, sizeof(tbuf
));
101 sputrace_tail
= (sputrace_tail
+ 1) % bufsize
;
102 spin_unlock(&sputrace_lock
);
107 error
= copy_to_user(buf
+ cnt
, tbuf
, width
);
113 return cnt
== 0 ? error
: cnt
;
116 static int sputrace_open(struct inode
*inode
, struct file
*file
)
120 spin_lock(&sputrace_lock
);
121 if (sputrace_logging
) {
126 sputrace_logging
= 1;
127 sputrace_head
= sputrace_tail
= 0;
128 sputrace_start
= ktime_get();
132 spin_unlock(&sputrace_lock
);
136 static int sputrace_release(struct inode
*inode
, struct file
*file
)
138 spin_lock(&sputrace_lock
);
139 sputrace_logging
= 0;
140 spin_unlock(&sputrace_lock
);
144 static const struct file_operations sputrace_fops
= {
145 .owner
= THIS_MODULE
,
146 .open
= sputrace_open
,
147 .read
= sputrace_read
,
148 .release
= sputrace_release
,
151 static void sputrace_log_item(const char *name
, struct spu_context
*ctx
,
154 spin_lock(&sputrace_lock
);
156 if (!sputrace_logging
) {
157 spin_unlock(&sputrace_lock
);
161 if (sputrace_avail() > 1) {
162 struct sputrace
*t
= sputrace_log
+ sputrace_head
;
164 t
->tstamp
= ktime_get();
165 t
->owner_tid
= ctx
->tid
;
167 t
->curr_tid
= current
->pid
;
168 t
->number
= spu
? spu
->number
: -1;
170 sputrace_head
= (sputrace_head
+ 1) % bufsize
;
173 "sputrace: lost samples due to full buffer.\n");
175 spin_unlock(&sputrace_lock
);
177 wake_up(&sputrace_wait
);
180 static void spu_context_event(void *probe_private
, void *call_data
,
181 const char *format
, va_list *args
)
183 struct spu_probe
*p
= probe_private
;
184 struct spu_context
*ctx
;
187 ctx
= va_arg(*args
, struct spu_context
*);
188 spu
= va_arg(*args
, struct spu
*);
190 sputrace_log_item(p
->name
, ctx
, spu
);
193 static void spu_context_nospu_event(void *probe_private
, void *call_data
,
194 const char *format
, va_list *args
)
196 struct spu_probe
*p
= probe_private
;
197 struct spu_context
*ctx
;
199 ctx
= va_arg(*args
, struct spu_context
*);
201 sputrace_log_item(p
->name
, ctx
, NULL
);
204 struct spu_probe spu_probes
[] = {
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
},
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
},
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
},
225 static int __init
sputrace_init(void)
227 struct proc_dir_entry
*entry
;
228 int i
, error
= -ENOMEM
;
230 sputrace_log
= kcalloc(bufsize
, sizeof(struct sputrace
), GFP_KERNEL
);
234 entry
= proc_create("sputrace", S_IRUSR
, NULL
, &sputrace_fops
);
238 for (i
= 0; i
< ARRAY_SIZE(spu_probes
); i
++) {
239 struct spu_probe
*p
= &spu_probes
[i
];
241 error
= marker_probe_register(p
->name
, p
->format
,
244 printk(KERN_INFO
"Unable to register probe %s\n",
256 static void __exit
sputrace_exit(void)
260 for (i
= 0; i
< ARRAY_SIZE(spu_probes
); i
++)
261 marker_probe_unregister(spu_probes
[i
].name
,
262 spu_probes
[i
].probe_func
, &spu_probes
[i
]);
264 remove_proc_entry("sputrace", NULL
);
266 marker_synchronize_unregister();
269 module_init(sputrace_init
);
270 module_exit(sputrace_exit
);
272 MODULE_LICENSE("GPL");