blob: 19a8c2c0389f1ab87dfe41193e04a5509f257130 [file] [log] [blame]
Markus Metzgereee3af42008-01-30 13:31:09 +01001/*
2 * Debug Store support
3 *
4 * This provides a low-level interface to the hardware's Debug Store
Markus Metzger93fa7632008-04-08 11:01:58 +02005 * feature that is used for branch trace store (BTS) and
Markus Metzgereee3af42008-01-30 13:31:09 +01006 * precise-event based sampling (PEBS).
7 *
Markus Metzger93fa7632008-04-08 11:01:58 +02008 * It manages:
9 * - per-thread and per-cpu allocation of BTS and PEBS
Markus Metzger6abb11a2008-11-25 09:05:27 +010010 * - buffer overflow handling (to be done)
Markus Metzger93fa7632008-04-08 11:01:58 +020011 * - buffer access
12 *
13 * It assumes:
Markus Metzger6abb11a2008-11-25 09:05:27 +010014 * - get_task_struct on all traced tasks
15 * - current is allowed to trace tasks
Markus Metzgereee3af42008-01-30 13:31:09 +010016 *
17 *
Markus Metzger93fa7632008-04-08 11:01:58 +020018 * Copyright (C) 2007-2008 Intel Corporation.
19 * Markus Metzger <markus.t.metzger@intel.com>, 2007-2008
Markus Metzgereee3af42008-01-30 13:31:09 +010020 */
21
Markus Metzger93fa7632008-04-08 11:01:58 +020022
Markus Metzgereee3af42008-01-30 13:31:09 +010023#include <asm/ds.h>
24
25#include <linux/errno.h>
26#include <linux/string.h>
27#include <linux/slab.h>
Markus Metzger93fa7632008-04-08 11:01:58 +020028#include <linux/sched.h>
Ingo Molnar3c9339042008-07-25 11:32:36 +020029#include <linux/mm.h>
Markus Metzgerca0002a2008-11-25 09:01:25 +010030#include <linux/kernel.h>
Markus Metzger93fa7632008-04-08 11:01:58 +020031
32
33/*
34 * The configuration for a particular DS hardware implementation.
35 */
36struct ds_configuration {
37 /* the size of the DS structure in bytes */
38 unsigned char sizeof_ds;
39 /* the size of one pointer-typed field in the DS structure in bytes;
40 this covers the first 8 fields related to buffer management. */
41 unsigned char sizeof_field;
42 /* the size of a BTS/PEBS record in bytes */
43 unsigned char sizeof_rec[2];
44};
45static struct ds_configuration ds_cfg;
Markus Metzgereee3af42008-01-30 13:31:09 +010046
Markus Metzgerca0002a2008-11-25 09:01:25 +010047/*
48 * A BTS or PEBS tracer.
49 *
50 * This holds the configuration of the tracer and serves as a handle
51 * to identify tracers.
52 */
53struct ds_tracer {
54 /* the DS context (partially) owned by this tracer */
55 struct ds_context *context;
56 /* the buffer provided on ds_request() and its size in bytes */
57 void *buffer;
58 size_t size;
Markus Metzgerca0002a2008-11-25 09:01:25 +010059};
60
61struct bts_tracer {
62 /* the common DS part */
63 struct ds_tracer ds;
64 /* buffer overflow notification function */
65 bts_ovfl_callback_t ovfl;
66};
67
68struct pebs_tracer {
69 /* the common DS part */
70 struct ds_tracer ds;
71 /* buffer overflow notification function */
72 pebs_ovfl_callback_t ovfl;
73};
Markus Metzgereee3af42008-01-30 13:31:09 +010074
75/*
76 * Debug Store (DS) save area configuration (see Intel64 and IA32
77 * Architectures Software Developer's Manual, section 18.5)
78 *
79 * The DS configuration consists of the following fields; different
80 * architetures vary in the size of those fields.
81 * - double-word aligned base linear address of the BTS buffer
82 * - write pointer into the BTS buffer
83 * - end linear address of the BTS buffer (one byte beyond the end of
84 * the buffer)
85 * - interrupt pointer into BTS buffer
86 * (interrupt occurs when write pointer passes interrupt pointer)
87 * - double-word aligned base linear address of the PEBS buffer
88 * - write pointer into the PEBS buffer
89 * - end linear address of the PEBS buffer (one byte beyond the end of
90 * the buffer)
91 * - interrupt pointer into PEBS buffer
92 * (interrupt occurs when write pointer passes interrupt pointer)
93 * - value to which counter is reset following counter overflow
94 *
Markus Metzger93fa7632008-04-08 11:01:58 +020095 * Later architectures use 64bit pointers throughout, whereas earlier
96 * architectures use 32bit pointers in 32bit mode.
Markus Metzgereee3af42008-01-30 13:31:09 +010097 *
98 *
Markus Metzger93fa7632008-04-08 11:01:58 +020099 * We compute the base address for the first 8 fields based on:
100 * - the field size stored in the DS configuration
101 * - the relative field position
102 * - an offset giving the start of the respective region
Markus Metzgereee3af42008-01-30 13:31:09 +0100103 *
Markus Metzger93fa7632008-04-08 11:01:58 +0200104 * This offset is further used to index various arrays holding
105 * information for BTS and PEBS at the respective index.
Markus Metzgereee3af42008-01-30 13:31:09 +0100106 *
Markus Metzger93fa7632008-04-08 11:01:58 +0200107 * On later 32bit processors, we only access the lower 32bit of the
108 * 64bit pointer fields. The upper halves will be zeroed out.
Markus Metzgereee3af42008-01-30 13:31:09 +0100109 */
110
Markus Metzger93fa7632008-04-08 11:01:58 +0200111enum ds_field {
112 ds_buffer_base = 0,
113 ds_index,
114 ds_absolute_maximum,
115 ds_interrupt_threshold,
Markus Metzgereee3af42008-01-30 13:31:09 +0100116};
117
Markus Metzger93fa7632008-04-08 11:01:58 +0200118enum ds_qualifier {
119 ds_bts = 0,
120 ds_pebs
Markus Metzgereee3af42008-01-30 13:31:09 +0100121};
122
Markus Metzger93fa7632008-04-08 11:01:58 +0200123static inline unsigned long ds_get(const unsigned char *base,
124 enum ds_qualifier qual, enum ds_field field)
125{
126 base += (ds_cfg.sizeof_field * (field + (4 * qual)));
127 return *(unsigned long *)base;
128}
129
130static inline void ds_set(unsigned char *base, enum ds_qualifier qual,
131 enum ds_field field, unsigned long value)
132{
133 base += (ds_cfg.sizeof_field * (field + (4 * qual)));
134 (*(unsigned long *)base) = value;
135}
136
Markus Metzgerca0002a2008-11-25 09:01:25 +0100137#define DS_ALIGNMENT (1 << 3) /* BTS and PEBS buffer alignment */
138
Markus Metzgereee3af42008-01-30 13:31:09 +0100139
140/*
Markus Metzger6abb11a2008-11-25 09:05:27 +0100141 * Locking is done only for allocating BTS or PEBS resources.
Markus Metzgereee3af42008-01-30 13:31:09 +0100142 */
Markus Metzger93fa7632008-04-08 11:01:58 +0200143static spinlock_t ds_lock = __SPIN_LOCK_UNLOCKED(ds_lock);
Markus Metzgereee3af42008-01-30 13:31:09 +0100144
Markus Metzger93fa7632008-04-08 11:01:58 +0200145
146/*
147 * We either support (system-wide) per-cpu or per-thread allocation.
148 * We distinguish the two based on the task_struct pointer, where a
149 * NULL pointer indicates per-cpu allocation for the current cpu.
150 *
151 * Allocations are use-counted. As soon as resources are allocated,
152 * further allocations must be of the same type (per-cpu or
153 * per-thread). We model this by counting allocations (i.e. the number
154 * of tracers of a certain type) for one type negatively:
155 * =0 no tracers
156 * >0 number of per-thread tracers
157 * <0 number of per-cpu tracers
158 *
159 * The below functions to get and put tracers and to check the
160 * allocation type require the ds_lock to be held by the caller.
161 *
162 * Tracers essentially gives the number of ds contexts for a certain
163 * type of allocation.
164 */
165static long tracers;
166
167static inline void get_tracer(struct task_struct *task)
Markus Metzgera95d67f2008-01-30 13:31:20 +0100168{
Markus Metzger93fa7632008-04-08 11:01:58 +0200169 tracers += (task ? 1 : -1);
Markus Metzgereee3af42008-01-30 13:31:09 +0100170}
171
Markus Metzger93fa7632008-04-08 11:01:58 +0200172static inline void put_tracer(struct task_struct *task)
Markus Metzgereee3af42008-01-30 13:31:09 +0100173{
Markus Metzger93fa7632008-04-08 11:01:58 +0200174 tracers -= (task ? 1 : -1);
Markus Metzgereee3af42008-01-30 13:31:09 +0100175}
176
Markus Metzger93fa7632008-04-08 11:01:58 +0200177static inline int check_tracer(struct task_struct *task)
Markus Metzgera95d67f2008-01-30 13:31:20 +0100178{
Markus Metzger93fa7632008-04-08 11:01:58 +0200179 return (task ? (tracers >= 0) : (tracers <= 0));
180}
181
182
183/*
184 * The DS context is either attached to a thread or to a cpu:
185 * - in the former case, the thread_struct contains a pointer to the
186 * attached context.
187 * - in the latter case, we use a static array of per-cpu context
188 * pointers.
189 *
190 * Contexts are use-counted. They are allocated on first access and
191 * deallocated when the last user puts the context.
Markus Metzger93fa7632008-04-08 11:01:58 +0200192 */
193static DEFINE_PER_CPU(struct ds_context *, system_context);
194
195#define this_system_context per_cpu(system_context, smp_processor_id())
196
Markus Metzger93fa7632008-04-08 11:01:58 +0200197static inline struct ds_context *ds_get_context(struct task_struct *task)
198{
Markus Metzger93fa7632008-04-08 11:01:58 +0200199 struct ds_context **p_context =
200 (task ? &task->thread.ds_ctx : &this_system_context);
201 struct ds_context *context = *p_context;
Markus Metzgerde90add2008-11-25 08:52:56 +0100202 unsigned long irq;
Markus Metzger93fa7632008-04-08 11:01:58 +0200203
204 if (!context) {
205 context = kzalloc(sizeof(*context), GFP_KERNEL);
Markus Metzgerde90add2008-11-25 08:52:56 +0100206 if (!context)
Cyrill Gorcunov573da422008-04-28 23:15:04 +0400207 return NULL;
Markus Metzger93fa7632008-04-08 11:01:58 +0200208
Markus Metzgerde90add2008-11-25 08:52:56 +0100209 spin_lock_irqsave(&ds_lock, irq);
210
Ingo Molnar10db4ef2008-11-18 15:23:08 +0100211 if (*p_context) {
Ingo Molnar10db4ef2008-11-18 15:23:08 +0100212 kfree(context);
Markus Metzgerde90add2008-11-25 08:52:56 +0100213
214 context = *p_context;
215 } else {
216 *p_context = context;
217
218 context->this = p_context;
219 context->task = task;
220
221 if (task)
222 set_tsk_thread_flag(task, TIF_DS_AREA_MSR);
223
224 if (!task || (task == current))
225 wrmsrl(MSR_IA32_DS_AREA,
226 (unsigned long)context->ds);
Ingo Molnar10db4ef2008-11-18 15:23:08 +0100227 }
Markus Metzgerde90add2008-11-25 08:52:56 +0100228 spin_unlock_irqrestore(&ds_lock, irq);
Markus Metzger93fa7632008-04-08 11:01:58 +0200229 }
230
231 context->count++;
232
233 return context;
234}
235
Markus Metzger93fa7632008-04-08 11:01:58 +0200236static inline void ds_put_context(struct ds_context *context)
237{
Markus Metzgerde90add2008-11-25 08:52:56 +0100238 unsigned long irq;
239
Markus Metzger93fa7632008-04-08 11:01:58 +0200240 if (!context)
241 return;
242
Markus Metzgerde90add2008-11-25 08:52:56 +0100243 spin_lock_irqsave(&ds_lock, irq);
Markus Metzger93fa7632008-04-08 11:01:58 +0200244
245 if (--context->count)
246 goto out;
247
Cyrill Gorcunov573da422008-04-28 23:15:04 +0400248 *(context->this) = NULL;
Markus Metzger93fa7632008-04-08 11:01:58 +0200249
250 if (context->task)
251 clear_tsk_thread_flag(context->task, TIF_DS_AREA_MSR);
252
253 if (!context->task || (context->task == current))
254 wrmsrl(MSR_IA32_DS_AREA, 0);
255
Markus Metzger93fa7632008-04-08 11:01:58 +0200256 kfree(context);
257 out:
Markus Metzgerde90add2008-11-25 08:52:56 +0100258 spin_unlock_irqrestore(&ds_lock, irq);
Markus Metzger93fa7632008-04-08 11:01:58 +0200259}
260
261
262/*
263 * Handle a buffer overflow
264 *
Markus Metzger93fa7632008-04-08 11:01:58 +0200265 * context: the ds context
266 * qual: the buffer type
267 */
Markus Metzgerca0002a2008-11-25 09:01:25 +0100268static void ds_overflow(struct ds_context *context, enum ds_qualifier qual)
Markus Metzger93fa7632008-04-08 11:01:58 +0200269{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100270 switch (qual) {
271 case ds_bts: {
272 struct bts_tracer *tracer =
273 container_of(context->owner[qual],
274 struct bts_tracer, ds);
275 if (tracer->ovfl)
276 tracer->ovfl(tracer);
277 }
278 break;
279 case ds_pebs: {
280 struct pebs_tracer *tracer =
281 container_of(context->owner[qual],
282 struct pebs_tracer, ds);
283 if (tracer->ovfl)
284 tracer->ovfl(tracer);
285 }
286 break;
287 }
Markus Metzger93fa7632008-04-08 11:01:58 +0200288}
289
290
Markus Metzgerca0002a2008-11-25 09:01:25 +0100291static void ds_install_ds_config(struct ds_context *context,
292 enum ds_qualifier qual,
293 void *base, size_t size, size_t ith)
Markus Metzgera95d67f2008-01-30 13:31:20 +0100294{
Markus Metzger93fa7632008-04-08 11:01:58 +0200295 unsigned long buffer, adj;
Markus Metzger93fa7632008-04-08 11:01:58 +0200296
297 /* adjust the buffer address and size to meet alignment
298 * constraints:
299 * - buffer is double-word aligned
300 * - size is multiple of record size
301 *
302 * We checked the size at the very beginning; we have enough
303 * space to do the adjustment.
304 */
305 buffer = (unsigned long)base;
306
Markus Metzgerca0002a2008-11-25 09:01:25 +0100307 adj = ALIGN(buffer, DS_ALIGNMENT) - buffer;
Markus Metzger93fa7632008-04-08 11:01:58 +0200308 buffer += adj;
309 size -= adj;
310
311 size /= ds_cfg.sizeof_rec[qual];
312 size *= ds_cfg.sizeof_rec[qual];
313
314 ds_set(context->ds, qual, ds_buffer_base, buffer);
315 ds_set(context->ds, qual, ds_index, buffer);
316 ds_set(context->ds, qual, ds_absolute_maximum, buffer + size);
317
Markus Metzgerca0002a2008-11-25 09:01:25 +0100318 /* The value for 'no threshold' is -1, which will set the
319 * threshold outside of the buffer, just like we want it.
320 */
321 ds_set(context->ds, qual,
322 ds_interrupt_threshold, buffer + size - ith);
323}
Markus Metzger93fa7632008-04-08 11:01:58 +0200324
Markus Metzgerca0002a2008-11-25 09:01:25 +0100325static int ds_request(struct ds_tracer *tracer, enum ds_qualifier qual,
326 struct task_struct *task,
327 void *base, size_t size, size_t th)
328{
329 struct ds_context *context;
330 unsigned long irq;
331 int error;
Markus Metzger93fa7632008-04-08 11:01:58 +0200332
Markus Metzgerca0002a2008-11-25 09:01:25 +0100333 error = -EOPNOTSUPP;
334 if (!ds_cfg.sizeof_ds)
335 goto out;
336
Markus Metzger6abb11a2008-11-25 09:05:27 +0100337 error = -EINVAL;
338 if (!base)
339 goto out;
340
Markus Metzgerca0002a2008-11-25 09:01:25 +0100341 /* we require some space to do alignment adjustments below */
342 error = -EINVAL;
343 if (size < (DS_ALIGNMENT + ds_cfg.sizeof_rec[qual]))
344 goto out;
345
346 if (th != (size_t)-1) {
347 th *= ds_cfg.sizeof_rec[qual];
348
349 error = -EINVAL;
350 if (size <= th)
351 goto out;
352 }
353
Markus Metzgerca0002a2008-11-25 09:01:25 +0100354 tracer->buffer = base;
355 tracer->size = size;
356
357 error = -ENOMEM;
358 context = ds_get_context(task);
359 if (!context)
360 goto out;
361 tracer->context = context;
362
363
364 spin_lock_irqsave(&ds_lock, irq);
365
366 error = -EPERM;
367 if (!check_tracer(task))
368 goto out_unlock;
369 get_tracer(task);
370
371 error = -EPERM;
372 if (context->owner[qual])
373 goto out_put_tracer;
374 context->owner[qual] = tracer;
375
376 spin_unlock_irqrestore(&ds_lock, irq);
377
378
379 ds_install_ds_config(context, qual, base, size, th);
380
381 return 0;
Markus Metzgerde90add2008-11-25 08:52:56 +0100382
383 out_put_tracer:
Markus Metzgerde90add2008-11-25 08:52:56 +0100384 put_tracer(task);
Markus Metzger93fa7632008-04-08 11:01:58 +0200385 out_unlock:
Markus Metzgerde90add2008-11-25 08:52:56 +0100386 spin_unlock_irqrestore(&ds_lock, irq);
Markus Metzger93fa7632008-04-08 11:01:58 +0200387 ds_put_context(context);
Markus Metzgerca0002a2008-11-25 09:01:25 +0100388 tracer->context = NULL;
Markus Metzger93fa7632008-04-08 11:01:58 +0200389 out:
Markus Metzger93fa7632008-04-08 11:01:58 +0200390 return error;
391}
392
Markus Metzgerca0002a2008-11-25 09:01:25 +0100393struct bts_tracer *ds_request_bts(struct task_struct *task,
394 void *base, size_t size,
395 bts_ovfl_callback_t ovfl, size_t th)
Markus Metzger93fa7632008-04-08 11:01:58 +0200396{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100397 struct bts_tracer *tracer;
Markus Metzger93fa7632008-04-08 11:01:58 +0200398 int error;
399
Markus Metzgerca0002a2008-11-25 09:01:25 +0100400 /* buffer overflow notification is not yet implemented */
401 error = -EOPNOTSUPP;
402 if (ovfl)
Markus Metzger93fa7632008-04-08 11:01:58 +0200403 goto out;
404
Markus Metzgerca0002a2008-11-25 09:01:25 +0100405 error = -ENOMEM;
406 tracer = kzalloc(sizeof(*tracer), GFP_KERNEL);
407 if (!tracer)
408 goto out;
409 tracer->ovfl = ovfl;
410
411 error = ds_request(&tracer->ds, ds_bts, task, base, size, th);
412 if (error < 0)
413 goto out_tracer;
414
415 return tracer;
416
417 out_tracer:
Markus Metzger6abb11a2008-11-25 09:05:27 +0100418 kfree(tracer);
Markus Metzgerca0002a2008-11-25 09:01:25 +0100419 out:
420 return ERR_PTR(error);
421}
422
423struct pebs_tracer *ds_request_pebs(struct task_struct *task,
424 void *base, size_t size,
425 pebs_ovfl_callback_t ovfl, size_t th)
426{
427 struct pebs_tracer *tracer;
428 int error;
429
430 /* buffer overflow notification is not yet implemented */
431 error = -EOPNOTSUPP;
432 if (ovfl)
433 goto out;
434
435 error = -ENOMEM;
436 tracer = kzalloc(sizeof(*tracer), GFP_KERNEL);
437 if (!tracer)
438 goto out;
439 tracer->ovfl = ovfl;
440
441 error = ds_request(&tracer->ds, ds_pebs, task, base, size, th);
442 if (error < 0)
443 goto out_tracer;
444
445 return tracer;
446
447 out_tracer:
Markus Metzger6abb11a2008-11-25 09:05:27 +0100448 kfree(tracer);
Markus Metzgerca0002a2008-11-25 09:01:25 +0100449 out:
450 return ERR_PTR(error);
451}
452
453static void ds_release(struct ds_tracer *tracer, enum ds_qualifier qual)
454{
Markus Metzger6abb11a2008-11-25 09:05:27 +0100455 BUG_ON(tracer->context->owner[qual] != tracer);
456 tracer->context->owner[qual] = NULL;
Markus Metzgerca0002a2008-11-25 09:01:25 +0100457
Markus Metzger6abb11a2008-11-25 09:05:27 +0100458 put_tracer(tracer->context->task);
459 ds_put_context(tracer->context);
Markus Metzgerca0002a2008-11-25 09:01:25 +0100460}
461
462int ds_release_bts(struct bts_tracer *tracer)
463{
464 if (!tracer)
465 return -EINVAL;
466
467 ds_release(&tracer->ds, ds_bts);
468 kfree(tracer);
469
470 return 0;
471}
472
473int ds_release_pebs(struct pebs_tracer *tracer)
474{
475 if (!tracer)
476 return -EINVAL;
477
478 ds_release(&tracer->ds, ds_pebs);
479 kfree(tracer);
480
481 return 0;
482}
483
484static size_t ds_get_index(struct ds_context *context, enum ds_qualifier qual)
485{
486 unsigned long base, index;
487
Markus Metzger93fa7632008-04-08 11:01:58 +0200488 base = ds_get(context->ds, qual, ds_buffer_base);
489 index = ds_get(context->ds, qual, ds_index);
490
Markus Metzgerca0002a2008-11-25 09:01:25 +0100491 return (index - base) / ds_cfg.sizeof_rec[qual];
Markus Metzger93fa7632008-04-08 11:01:58 +0200492}
493
Markus Metzgerca0002a2008-11-25 09:01:25 +0100494int ds_get_bts_index(struct bts_tracer *tracer, size_t *pos)
Markus Metzger93fa7632008-04-08 11:01:58 +0200495{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100496 if (!tracer)
497 return -EINVAL;
498
499 if (!pos)
500 return -EINVAL;
501
502 *pos = ds_get_index(tracer->ds.context, ds_bts);
503
504 return 0;
Markus Metzger93fa7632008-04-08 11:01:58 +0200505}
506
Markus Metzgerca0002a2008-11-25 09:01:25 +0100507int ds_get_pebs_index(struct pebs_tracer *tracer, size_t *pos)
Markus Metzger93fa7632008-04-08 11:01:58 +0200508{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100509 if (!tracer)
510 return -EINVAL;
511
512 if (!pos)
513 return -EINVAL;
514
515 *pos = ds_get_index(tracer->ds.context, ds_pebs);
516
517 return 0;
Markus Metzger93fa7632008-04-08 11:01:58 +0200518}
519
Markus Metzgerca0002a2008-11-25 09:01:25 +0100520static size_t ds_get_end(struct ds_context *context, enum ds_qualifier qual)
Markus Metzger93fa7632008-04-08 11:01:58 +0200521{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100522 unsigned long base, max;
Markus Metzger93fa7632008-04-08 11:01:58 +0200523
524 base = ds_get(context->ds, qual, ds_buffer_base);
Markus Metzgerca0002a2008-11-25 09:01:25 +0100525 max = ds_get(context->ds, qual, ds_absolute_maximum);
Markus Metzger93fa7632008-04-08 11:01:58 +0200526
Markus Metzgerca0002a2008-11-25 09:01:25 +0100527 return (max - base) / ds_cfg.sizeof_rec[qual];
Markus Metzger93fa7632008-04-08 11:01:58 +0200528}
529
Markus Metzgerca0002a2008-11-25 09:01:25 +0100530int ds_get_bts_end(struct bts_tracer *tracer, size_t *pos)
Markus Metzger93fa7632008-04-08 11:01:58 +0200531{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100532 if (!tracer)
533 return -EINVAL;
534
535 if (!pos)
536 return -EINVAL;
537
538 *pos = ds_get_end(tracer->ds.context, ds_bts);
539
540 return 0;
Markus Metzger93fa7632008-04-08 11:01:58 +0200541}
542
Markus Metzgerca0002a2008-11-25 09:01:25 +0100543int ds_get_pebs_end(struct pebs_tracer *tracer, size_t *pos)
Markus Metzger93fa7632008-04-08 11:01:58 +0200544{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100545 if (!tracer)
546 return -EINVAL;
547
548 if (!pos)
549 return -EINVAL;
550
551 *pos = ds_get_end(tracer->ds.context, ds_pebs);
552
553 return 0;
Markus Metzger93fa7632008-04-08 11:01:58 +0200554}
555
Markus Metzgerca0002a2008-11-25 09:01:25 +0100556static int ds_access(struct ds_context *context, enum ds_qualifier qual,
557 size_t index, const void **record)
Markus Metzger93fa7632008-04-08 11:01:58 +0200558{
Markus Metzger93fa7632008-04-08 11:01:58 +0200559 unsigned long base, idx;
Markus Metzger93fa7632008-04-08 11:01:58 +0200560
561 if (!record)
Markus Metzgereee3af42008-01-30 13:31:09 +0100562 return -EINVAL;
Markus Metzger93fa7632008-04-08 11:01:58 +0200563
Markus Metzger93fa7632008-04-08 11:01:58 +0200564 base = ds_get(context->ds, qual, ds_buffer_base);
565 idx = base + (index * ds_cfg.sizeof_rec[qual]);
566
Markus Metzger93fa7632008-04-08 11:01:58 +0200567 if (idx > ds_get(context->ds, qual, ds_absolute_maximum))
Markus Metzgerca0002a2008-11-25 09:01:25 +0100568 return -EINVAL;
Markus Metzger93fa7632008-04-08 11:01:58 +0200569
570 *record = (const void *)idx;
Markus Metzgerca0002a2008-11-25 09:01:25 +0100571
572 return ds_cfg.sizeof_rec[qual];
Markus Metzger93fa7632008-04-08 11:01:58 +0200573}
574
Markus Metzgerca0002a2008-11-25 09:01:25 +0100575int ds_access_bts(struct bts_tracer *tracer, size_t index,
576 const void **record)
Markus Metzger93fa7632008-04-08 11:01:58 +0200577{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100578 if (!tracer)
579 return -EINVAL;
580
581 return ds_access(tracer->ds.context, ds_bts, index, record);
Markus Metzger93fa7632008-04-08 11:01:58 +0200582}
583
Markus Metzgerca0002a2008-11-25 09:01:25 +0100584int ds_access_pebs(struct pebs_tracer *tracer, size_t index,
585 const void **record)
Markus Metzger93fa7632008-04-08 11:01:58 +0200586{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100587 if (!tracer)
588 return -EINVAL;
589
590 return ds_access(tracer->ds.context, ds_pebs, index, record);
Markus Metzger93fa7632008-04-08 11:01:58 +0200591}
592
Markus Metzgerca0002a2008-11-25 09:01:25 +0100593static int ds_write(struct ds_context *context, enum ds_qualifier qual,
594 const void *record, size_t size)
Markus Metzger93fa7632008-04-08 11:01:58 +0200595{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100596 int bytes_written = 0;
Markus Metzger93fa7632008-04-08 11:01:58 +0200597
598 if (!record)
599 return -EINVAL;
600
Markus Metzger93fa7632008-04-08 11:01:58 +0200601 while (size) {
602 unsigned long base, index, end, write_end, int_th;
603 unsigned long write_size, adj_write_size;
Markus Metzgereee3af42008-01-30 13:31:09 +0100604
Markus Metzger93fa7632008-04-08 11:01:58 +0200605 /*
606 * write as much as possible without producing an
607 * overflow interrupt.
608 *
609 * interrupt_threshold must either be
610 * - bigger than absolute_maximum or
611 * - point to a record between buffer_base and absolute_maximum
612 *
613 * index points to a valid record.
614 */
615 base = ds_get(context->ds, qual, ds_buffer_base);
616 index = ds_get(context->ds, qual, ds_index);
617 end = ds_get(context->ds, qual, ds_absolute_maximum);
618 int_th = ds_get(context->ds, qual, ds_interrupt_threshold);
619
620 write_end = min(end, int_th);
621
622 /* if we are already beyond the interrupt threshold,
623 * we fill the entire buffer */
624 if (write_end <= index)
625 write_end = end;
626
627 if (write_end <= index)
Markus Metzgerca0002a2008-11-25 09:01:25 +0100628 break;
Markus Metzger93fa7632008-04-08 11:01:58 +0200629
630 write_size = min((unsigned long) size, write_end - index);
631 memcpy((void *)index, record, write_size);
632
633 record = (const char *)record + write_size;
Markus Metzgerca0002a2008-11-25 09:01:25 +0100634 size -= write_size;
635 bytes_written += write_size;
Markus Metzger93fa7632008-04-08 11:01:58 +0200636
637 adj_write_size = write_size / ds_cfg.sizeof_rec[qual];
638 adj_write_size *= ds_cfg.sizeof_rec[qual];
639
640 /* zero out trailing bytes */
641 memset((char *)index + write_size, 0,
642 adj_write_size - write_size);
643 index += adj_write_size;
644
645 if (index >= end)
646 index = base;
647 ds_set(context->ds, qual, ds_index, index);
648
649 if (index >= int_th)
Markus Metzgerca0002a2008-11-25 09:01:25 +0100650 ds_overflow(context, qual);
Markus Metzger93fa7632008-04-08 11:01:58 +0200651 }
652
Markus Metzgerca0002a2008-11-25 09:01:25 +0100653 return bytes_written;
Markus Metzgereee3af42008-01-30 13:31:09 +0100654}
655
Markus Metzgerca0002a2008-11-25 09:01:25 +0100656int ds_write_bts(struct bts_tracer *tracer, const void *record, size_t size)
Markus Metzgereee3af42008-01-30 13:31:09 +0100657{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100658 if (!tracer)
659 return -EINVAL;
660
661 return ds_write(tracer->ds.context, ds_bts, record, size);
Markus Metzgereee3af42008-01-30 13:31:09 +0100662}
663
Markus Metzgerca0002a2008-11-25 09:01:25 +0100664int ds_write_pebs(struct pebs_tracer *tracer, const void *record, size_t size)
Markus Metzger93fa7632008-04-08 11:01:58 +0200665{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100666 if (!tracer)
667 return -EINVAL;
668
669 return ds_write(tracer->ds.context, ds_pebs, record, size);
Markus Metzger93fa7632008-04-08 11:01:58 +0200670}
Markus Metzgereee3af42008-01-30 13:31:09 +0100671
Markus Metzgerca0002a2008-11-25 09:01:25 +0100672static void ds_reset_or_clear(struct ds_context *context,
673 enum ds_qualifier qual, int clear)
Markus Metzger93fa7632008-04-08 11:01:58 +0200674{
Markus Metzger93fa7632008-04-08 11:01:58 +0200675 unsigned long base, end;
Markus Metzger93fa7632008-04-08 11:01:58 +0200676
677 base = ds_get(context->ds, qual, ds_buffer_base);
678 end = ds_get(context->ds, qual, ds_absolute_maximum);
679
680 if (clear)
681 memset((void *)base, 0, end - base);
682
683 ds_set(context->ds, qual, ds_index, base);
Markus Metzger93fa7632008-04-08 11:01:58 +0200684}
685
Markus Metzgerca0002a2008-11-25 09:01:25 +0100686int ds_reset_bts(struct bts_tracer *tracer)
Markus Metzger93fa7632008-04-08 11:01:58 +0200687{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100688 if (!tracer)
689 return -EINVAL;
690
691 ds_reset_or_clear(tracer->ds.context, ds_bts, /* clear = */ 0);
692
693 return 0;
Markus Metzger93fa7632008-04-08 11:01:58 +0200694}
695
Markus Metzgerca0002a2008-11-25 09:01:25 +0100696int ds_reset_pebs(struct pebs_tracer *tracer)
Markus Metzger93fa7632008-04-08 11:01:58 +0200697{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100698 if (!tracer)
699 return -EINVAL;
700
701 ds_reset_or_clear(tracer->ds.context, ds_pebs, /* clear = */ 0);
702
703 return 0;
Markus Metzger93fa7632008-04-08 11:01:58 +0200704}
705
Markus Metzgerca0002a2008-11-25 09:01:25 +0100706int ds_clear_bts(struct bts_tracer *tracer)
Markus Metzger93fa7632008-04-08 11:01:58 +0200707{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100708 if (!tracer)
709 return -EINVAL;
710
711 ds_reset_or_clear(tracer->ds.context, ds_bts, /* clear = */ 1);
712
713 return 0;
Markus Metzger93fa7632008-04-08 11:01:58 +0200714}
715
Markus Metzgerca0002a2008-11-25 09:01:25 +0100716int ds_clear_pebs(struct pebs_tracer *tracer)
Markus Metzger93fa7632008-04-08 11:01:58 +0200717{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100718 if (!tracer)
719 return -EINVAL;
720
721 ds_reset_or_clear(tracer->ds.context, ds_pebs, /* clear = */ 1);
722
723 return 0;
Markus Metzger93fa7632008-04-08 11:01:58 +0200724}
725
Markus Metzgerca0002a2008-11-25 09:01:25 +0100726int ds_get_pebs_reset(struct pebs_tracer *tracer, u64 *value)
Markus Metzger93fa7632008-04-08 11:01:58 +0200727{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100728 if (!tracer)
729 return -EINVAL;
Markus Metzger93fa7632008-04-08 11:01:58 +0200730
731 if (!value)
732 return -EINVAL;
733
Markus Metzgerca0002a2008-11-25 09:01:25 +0100734 *value = *(u64 *)(tracer->ds.context->ds + (ds_cfg.sizeof_field * 8));
Markus Metzger93fa7632008-04-08 11:01:58 +0200735
Markus Metzgerca0002a2008-11-25 09:01:25 +0100736 return 0;
Markus Metzger93fa7632008-04-08 11:01:58 +0200737}
738
Markus Metzgerca0002a2008-11-25 09:01:25 +0100739int ds_set_pebs_reset(struct pebs_tracer *tracer, u64 value)
Markus Metzger93fa7632008-04-08 11:01:58 +0200740{
Markus Metzgerca0002a2008-11-25 09:01:25 +0100741 if (!tracer)
742 return -EINVAL;
Markus Metzger93fa7632008-04-08 11:01:58 +0200743
Markus Metzgerca0002a2008-11-25 09:01:25 +0100744 *(u64 *)(tracer->ds.context->ds + (ds_cfg.sizeof_field * 8)) = value;
Markus Metzger93fa7632008-04-08 11:01:58 +0200745
Markus Metzgerca0002a2008-11-25 09:01:25 +0100746 return 0;
Markus Metzger93fa7632008-04-08 11:01:58 +0200747}
748
749static const struct ds_configuration ds_cfg_var = {
750 .sizeof_ds = sizeof(long) * 12,
751 .sizeof_field = sizeof(long),
752 .sizeof_rec[ds_bts] = sizeof(long) * 3,
Markus Metzgerc4858ff2008-11-25 08:49:06 +0100753#ifdef __i386__
Markus Metzger93fa7632008-04-08 11:01:58 +0200754 .sizeof_rec[ds_pebs] = sizeof(long) * 10
Markus Metzgerc4858ff2008-11-25 08:49:06 +0100755#else
756 .sizeof_rec[ds_pebs] = sizeof(long) * 18
757#endif
Markus Metzger93fa7632008-04-08 11:01:58 +0200758};
759static const struct ds_configuration ds_cfg_64 = {
760 .sizeof_ds = 8 * 12,
761 .sizeof_field = 8,
762 .sizeof_rec[ds_bts] = 8 * 3,
Markus Metzgerc4858ff2008-11-25 08:49:06 +0100763#ifdef __i386__
Markus Metzger93fa7632008-04-08 11:01:58 +0200764 .sizeof_rec[ds_pebs] = 8 * 10
Markus Metzgerc4858ff2008-11-25 08:49:06 +0100765#else
766 .sizeof_rec[ds_pebs] = 8 * 18
767#endif
Markus Metzgereee3af42008-01-30 13:31:09 +0100768};
769
770static inline void
771ds_configure(const struct ds_configuration *cfg)
772{
773 ds_cfg = *cfg;
Markus Metzgerca0002a2008-11-25 09:01:25 +0100774
775 printk(KERN_INFO "DS available\n");
776
777 BUG_ON(MAX_SIZEOF_DS < ds_cfg.sizeof_ds);
Markus Metzgereee3af42008-01-30 13:31:09 +0100778}
779
780void __cpuinit ds_init_intel(struct cpuinfo_x86 *c)
781{
782 switch (c->x86) {
783 case 0x6:
784 switch (c->x86_model) {
Markus Metzgerf4166c52008-11-09 14:29:21 +0100785 case 0 ... 0xC:
786 /* sorry, don't know about them */
787 break;
Markus Metzgereee3af42008-01-30 13:31:09 +0100788 case 0xD:
789 case 0xE: /* Pentium M */
Markus Metzger93fa7632008-04-08 11:01:58 +0200790 ds_configure(&ds_cfg_var);
Markus Metzgereee3af42008-01-30 13:31:09 +0100791 break;
Markus Metzgerf4166c52008-11-09 14:29:21 +0100792 default: /* Core2, Atom, ... */
Markus Metzger93fa7632008-04-08 11:01:58 +0200793 ds_configure(&ds_cfg_64);
Markus Metzgereee3af42008-01-30 13:31:09 +0100794 break;
Markus Metzgereee3af42008-01-30 13:31:09 +0100795 }
796 break;
797 case 0xF:
798 switch (c->x86_model) {
Markus Metzgereee3af42008-01-30 13:31:09 +0100799 case 0x0:
800 case 0x1:
801 case 0x2: /* Netburst */
Markus Metzger93fa7632008-04-08 11:01:58 +0200802 ds_configure(&ds_cfg_var);
Markus Metzgereee3af42008-01-30 13:31:09 +0100803 break;
Markus Metzgereee3af42008-01-30 13:31:09 +0100804 default:
805 /* sorry, don't know about them */
806 break;
807 }
808 break;
809 default:
810 /* sorry, don't know about them */
811 break;
812 }
813}
Markus Metzger93fa7632008-04-08 11:01:58 +0200814
815void ds_free(struct ds_context *context)
816{
817 /* This is called when the task owning the parameter context
818 * is dying. There should not be any user of that context left
819 * to disturb us, anymore. */
820 unsigned long leftovers = context->count;
Markus Metzgerca0002a2008-11-25 09:01:25 +0100821 while (leftovers--) {
822 put_tracer(context->task);
Markus Metzger93fa7632008-04-08 11:01:58 +0200823 ds_put_context(context);
Markus Metzgerca0002a2008-11-25 09:01:25 +0100824 }
Markus Metzger93fa7632008-04-08 11:01:58 +0200825}