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
;
44 static int sputrace_used(void)
46 return (sputrace_head
- sputrace_tail
) % bufsize
;
49 static inline int sputrace_avail(void)
51 return bufsize
- sputrace_used();
54 static int sputrace_sprint(char *tbuf
, int n
)
56 const struct sputrace
*t
= sputrace_log
+ sputrace_tail
% bufsize
;
58 ktime_to_timespec(ktime_sub(t
->tstamp
, sputrace_start
));
60 return snprintf(tbuf
, n
,
61 "[%lu.%09lu] %d: %s (ctxthread = %d, spu = %d)\n",
62 (unsigned long) tv
.tv_sec
,
63 (unsigned long) tv
.tv_nsec
,
70 static ssize_t
sputrace_read(struct file
*file
, char __user
*buf
,
71 size_t len
, loff_t
*ppos
)
73 int error
= 0, cnt
= 0;
82 error
= wait_event_interruptible(sputrace_wait
,
87 spin_lock(&sputrace_lock
);
88 if (sputrace_head
== sputrace_tail
) {
89 spin_unlock(&sputrace_lock
);
93 width
= sputrace_sprint(tbuf
, sizeof(tbuf
));
95 sputrace_tail
= (sputrace_tail
+ 1) % bufsize
;
96 spin_unlock(&sputrace_lock
);
101 error
= copy_to_user(buf
+ cnt
, tbuf
, width
);
107 return cnt
== 0 ? error
: cnt
;
110 static int sputrace_open(struct inode
*inode
, struct file
*file
)
112 spin_lock(&sputrace_lock
);
113 sputrace_head
= sputrace_tail
= 0;
114 sputrace_start
= ktime_get();
115 spin_unlock(&sputrace_lock
);
120 static const struct file_operations sputrace_fops
= {
121 .owner
= THIS_MODULE
,
122 .open
= sputrace_open
,
123 .read
= sputrace_read
,
126 static void sputrace_log_item(const char *name
, struct spu_context
*ctx
,
129 spin_lock(&sputrace_lock
);
130 if (sputrace_avail() > 1) {
131 struct sputrace
*t
= sputrace_log
+ sputrace_head
;
133 t
->tstamp
= ktime_get();
134 t
->owner_tid
= ctx
->tid
;
136 t
->curr_tid
= current
->pid
;
137 t
->number
= spu
? spu
->number
: -1;
139 sputrace_head
= (sputrace_head
+ 1) % bufsize
;
142 "sputrace: lost samples due to full buffer.\n");
144 spin_unlock(&sputrace_lock
);
146 wake_up(&sputrace_wait
);
149 static void spu_context_event(void *probe_private
, void *call_data
,
150 const char *format
, va_list *args
)
152 struct spu_probe
*p
= probe_private
;
153 struct spu_context
*ctx
;
156 ctx
= va_arg(*args
, struct spu_context
*);
157 spu
= va_arg(*args
, struct spu
*);
159 sputrace_log_item(p
->name
, ctx
, spu
);
162 static void spu_context_nospu_event(void *probe_private
, void *call_data
,
163 const char *format
, va_list *args
)
165 struct spu_probe
*p
= probe_private
;
166 struct spu_context
*ctx
;
168 ctx
= va_arg(*args
, struct spu_context
*);
170 sputrace_log_item(p
->name
, ctx
, NULL
);
173 struct spu_probe spu_probes
[] = {
174 { "spu_bind_context__enter", "%p %p", spu_context_event
},
175 { "spu_unbind_context__enter", "%p %p", spu_context_event
},
176 { "spu_get_idle__enter", "%p", spu_context_nospu_event
},
177 { "spu_get_idle__found", "%p %p", spu_context_event
},
178 { "spu_get_idle__not_found", "%p", spu_context_nospu_event
},
179 { "spu_find_victim__enter", "%p", spu_context_nospu_event
},
180 { "spusched_tick__preempt", "%p %p", spu_context_event
},
181 { "spusched_tick__newslice", "%p", spu_context_nospu_event
},
182 { "spu_yield__enter", "%p", spu_context_nospu_event
},
183 { "spu_deactivate__enter", "%p", spu_context_nospu_event
},
184 { "__spu_deactivate__unload", "%p %p", spu_context_event
},
185 { "spufs_ps_nopfn__enter", "%p", spu_context_nospu_event
},
186 { "spufs_ps_nopfn__sleep", "%p", spu_context_nospu_event
},
187 { "spufs_ps_nopfn__wake", "%p %p", spu_context_event
},
188 { "spufs_ps_nopfn__insert", "%p %p", spu_context_event
},
189 { "spu_acquire_saved__enter", "%p", spu_context_nospu_event
},
190 { "destroy_spu_context__enter", "%p", spu_context_nospu_event
},
191 { "spufs_stop_callback__enter", "%p %p", spu_context_event
},
194 static int __init
sputrace_init(void)
196 struct proc_dir_entry
*entry
;
197 int i
, error
= -ENOMEM
;
199 sputrace_log
= kcalloc(sizeof(struct sputrace
),
200 bufsize
, GFP_KERNEL
);
204 entry
= create_proc_entry("sputrace", S_IRUSR
, NULL
);
207 entry
->proc_fops
= &sputrace_fops
;
209 for (i
= 0; i
< ARRAY_SIZE(spu_probes
); i
++) {
210 struct spu_probe
*p
= &spu_probes
[i
];
212 error
= marker_probe_register(p
->name
, p
->format
,
215 printk(KERN_INFO
"Unable to register probe %s\n",
227 static void __exit
sputrace_exit(void)
231 for (i
= 0; i
< ARRAY_SIZE(spu_probes
); i
++)
232 marker_probe_unregister(spu_probes
[i
].name
,
233 spu_probes
[i
].probe_func
, &spu_probes
[i
]);
235 remove_proc_entry("sputrace", NULL
);
239 module_init(sputrace_init
);
240 module_exit(sputrace_exit
);
242 MODULE_LICENSE("GPL");