aboutsummaryrefslogtreecommitdiffstats
path: root/litmus/sched_trace.c
diff options
context:
space:
mode:
Diffstat (limited to 'litmus/sched_trace.c')
-rw-r--r--litmus/sched_trace.c251
1 files changed, 251 insertions, 0 deletions
diff --git a/litmus/sched_trace.c b/litmus/sched_trace.c
new file mode 100644
index 000000000000..426a9dd6af65
--- /dev/null
+++ b/litmus/sched_trace.c
@@ -0,0 +1,251 @@
1/*
2 * sched_trace.c -- record scheduling events to a byte stream.
3 */
4#include <linux/spinlock.h>
5#include <linux/mutex.h>
6
7#include <linux/fs.h>
8#include <linux/slab.h>
9#include <linux/miscdevice.h>
10#include <asm/uaccess.h>
11#include <linux/module.h>
12#include <linux/sysrq.h>
13#include <linux/sched.h>
14#include <linux/kfifo.h>
15
16atomic_t __log_seq_no = ATOMIC_INIT(0);
17
18#define SCHED_TRACE_NAME "litmus/log"
19
20/* Compute size of TRACE() buffer */
21#define LITMUS_TRACE_BUF_SIZE (1 << CONFIG_SCHED_DEBUG_TRACE_SHIFT)
22
23/* Max length of one read from the buffer */
24#define MAX_READ_LEN (64 * 1024)
25
26/* Max length for one write --- by TRACE() --- to the buffer. This is used to
27 * allocate a per-cpu buffer for printf() formatting. */
28#define MSG_SIZE 255
29
30
31static DEFINE_MUTEX(reader_mutex);
32static atomic_t reader_cnt = ATOMIC_INIT(0);
33static DEFINE_KFIFO(debug_buffer, char, LITMUS_TRACE_BUF_SIZE);
34
35
36static DEFINE_RAW_SPINLOCK(log_buffer_lock);
37static DEFINE_PER_CPU(char[MSG_SIZE], fmt_buffer);
38
39/*
40 * sched_trace_log_message - Write to the trace buffer (log_buffer)
41 *
42 * This is the only function accessing the log_buffer from inside the
43 * kernel for writing.
44 * Concurrent access to sched_trace_log_message must be serialized using
45 * log_buffer_lock
46 * The maximum length of a formatted message is 255
47 */
48void sched_trace_log_message(const char* fmt, ...)
49{
50 unsigned long flags;
51 va_list args;
52 size_t len;
53 char* buf;
54
55 if (!atomic_read(&reader_cnt))
56 /* early exit if nobody is listening */
57 return;
58
59 va_start(args, fmt);
60 local_irq_save(flags);
61
62 /* format message */
63 buf = __get_cpu_var(fmt_buffer);
64 len = vscnprintf(buf, MSG_SIZE, fmt, args);
65
66 raw_spin_lock(&log_buffer_lock);
67 /* Don't copy the trailing null byte, we don't want null bytes in a
68 * text file.
69 */
70 kfifo_in(&debug_buffer, buf, len);
71 raw_spin_unlock(&log_buffer_lock);
72
73 local_irq_restore(flags);
74 va_end(args);
75}
76
77
78/*
79 * log_read - Read the trace buffer
80 *
81 * This function is called as a file operation from userspace.
82 * Readers can sleep. Access is serialized through reader_mutex
83 */
84static ssize_t log_read(struct file *filp,
85 char __user *to, size_t len,
86 loff_t *f_pos)
87{
88 /* we ignore f_pos, this is strictly sequential */
89
90 ssize_t error = -EINVAL;
91 char* mem;
92
93 if (mutex_lock_interruptible(&reader_mutex)) {
94 error = -ERESTARTSYS;
95 goto out;
96 }
97
98 if (len > MAX_READ_LEN)
99 len = MAX_READ_LEN;
100
101 mem = kmalloc(len, GFP_KERNEL);
102 if (!mem) {
103 error = -ENOMEM;
104 goto out_unlock;
105 }
106
107 error = kfifo_out(&debug_buffer, mem, len);
108 while (!error) {
109 set_current_state(TASK_INTERRUPTIBLE);
110 schedule_timeout(110);
111 if (signal_pending(current))
112 error = -ERESTARTSYS;
113 else
114 error = kfifo_out(&debug_buffer, mem, len);
115 }
116
117 if (error > 0 && copy_to_user(to, mem, error))
118 error = -EFAULT;
119
120 kfree(mem);
121 out_unlock:
122 mutex_unlock(&reader_mutex);
123 out:
124 return error;
125}
126
127/*
128 * Enable redirection of printk() messages to the trace buffer.
129 * Defined in kernel/printk.c
130 */
131extern int trace_override;
132extern int trace_recurse;
133
134/*
135 * log_open - open the global log message ring buffer.
136 */
137static int log_open(struct inode *in, struct file *filp)
138{
139 int error = -EINVAL;
140
141 if (mutex_lock_interruptible(&reader_mutex)) {
142 error = -ERESTARTSYS;
143 goto out;
144 }
145
146 atomic_inc(&reader_cnt);
147 error = 0;
148
149 printk(KERN_DEBUG
150 "sched_trace kfifo with buffer starting at: 0x%p\n",
151 debug_buffer.buf);
152
153 /* override printk() */
154 trace_override++;
155
156 mutex_unlock(&reader_mutex);
157 out:
158 return error;
159}
160
161static int log_release(struct inode *in, struct file *filp)
162{
163 int error = -EINVAL;
164
165 if (mutex_lock_interruptible(&reader_mutex)) {
166 error = -ERESTARTSYS;
167 goto out;
168 }
169
170 atomic_dec(&reader_cnt);
171
172 /* release printk() overriding */
173 trace_override--;
174
175 printk(KERN_DEBUG "sched_trace kfifo released\n");
176
177 mutex_unlock(&reader_mutex);
178 out:
179 return error;
180}
181
182/*
183 * log_fops - The file operations for accessing the global LITMUS log message
184 * buffer.
185 *
186 * Except for opening the device file it uses the same operations as trace_fops.
187 */
188static struct file_operations log_fops = {
189 .owner = THIS_MODULE,
190 .open = log_open,
191 .release = log_release,
192 .read = log_read,
193};
194
195static struct miscdevice litmus_log_dev = {
196 .name = SCHED_TRACE_NAME,
197 .minor = MISC_DYNAMIC_MINOR,
198 .fops = &log_fops,
199};
200
201#ifdef CONFIG_MAGIC_SYSRQ
202void dump_trace_buffer(int max)
203{
204 char line[80];
205 int len;
206 int count = 0;
207
208 /* potential, but very unlikely, race... */
209 trace_recurse = 1;
210 while ((max == 0 || count++ < max) &&
211 (len = kfifo_out(&debug_buffer, line, sizeof(line - 1))) > 0) {
212 line[len] = '\0';
213 printk("%s", line);
214 }
215 trace_recurse = 0;
216}
217
218static void sysrq_dump_trace_buffer(int key)
219{
220 dump_trace_buffer(100);
221}
222
223static struct sysrq_key_op sysrq_dump_trace_buffer_op = {
224 .handler = sysrq_dump_trace_buffer,
225 .help_msg = "dump-trace-buffer(Y)",
226 .action_msg = "writing content of TRACE() buffer",
227};
228#endif
229
230static int __init init_sched_trace(void)
231{
232 printk("Initializing TRACE() device\n");
233
234#ifdef CONFIG_MAGIC_SYSRQ
235 /* offer some debugging help */
236 if (!register_sysrq_key('y', &sysrq_dump_trace_buffer_op))
237 printk("Registered dump-trace-buffer(Y) magic sysrq.\n");
238 else
239 printk("Could not register dump-trace-buffer(Y) magic sysrq.\n");
240#endif
241
242 return misc_register(&litmus_log_dev);
243}
244
245static void __exit exit_sched_trace(void)
246{
247 misc_deregister(&litmus_log_dev);
248}
249
250module_init(init_sched_trace);
251module_exit(exit_sched_trace);