diff options
Diffstat (limited to 'arch/x86/kernel/ds.c')
-rw-r--r-- | arch/x86/kernel/ds.c | 692 |
1 files changed, 314 insertions, 378 deletions
diff --git a/arch/x86/kernel/ds.c b/arch/x86/kernel/ds.c index a2d1176c38ee..19a8c2c0389f 100644 --- a/arch/x86/kernel/ds.c +++ b/arch/x86/kernel/ds.c | |||
@@ -7,13 +7,12 @@ | |||
7 | * | 7 | * |
8 | * It manages: | 8 | * It manages: |
9 | * - per-thread and per-cpu allocation of BTS and PEBS | 9 | * - per-thread and per-cpu allocation of BTS and PEBS |
10 | * - buffer memory allocation (optional) | 10 | * - buffer overflow handling (to be done) |
11 | * - buffer overflow handling | ||
12 | * - buffer access | 11 | * - buffer access |
13 | * | 12 | * |
14 | * It assumes: | 13 | * It assumes: |
15 | * - get_task_struct on all parameter tasks | 14 | * - get_task_struct on all traced tasks |
16 | * - current is allowed to trace parameter tasks | 15 | * - current is allowed to trace tasks |
17 | * | 16 | * |
18 | * | 17 | * |
19 | * Copyright (C) 2007-2008 Intel Corporation. | 18 | * Copyright (C) 2007-2008 Intel Corporation. |
@@ -28,6 +27,7 @@ | |||
28 | #include <linux/slab.h> | 27 | #include <linux/slab.h> |
29 | #include <linux/sched.h> | 28 | #include <linux/sched.h> |
30 | #include <linux/mm.h> | 29 | #include <linux/mm.h> |
30 | #include <linux/kernel.h> | ||
31 | 31 | ||
32 | 32 | ||
33 | /* | 33 | /* |
@@ -44,6 +44,33 @@ struct ds_configuration { | |||
44 | }; | 44 | }; |
45 | static struct ds_configuration ds_cfg; | 45 | static struct ds_configuration ds_cfg; |
46 | 46 | ||
47 | /* | ||
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 | */ | ||
53 | struct 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; | ||
59 | }; | ||
60 | |||
61 | struct 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 | |||
68 | struct pebs_tracer { | ||
69 | /* the common DS part */ | ||
70 | struct ds_tracer ds; | ||
71 | /* buffer overflow notification function */ | ||
72 | pebs_ovfl_callback_t ovfl; | ||
73 | }; | ||
47 | 74 | ||
48 | /* | 75 | /* |
49 | * Debug Store (DS) save area configuration (see Intel64 and IA32 | 76 | * Debug Store (DS) save area configuration (see Intel64 and IA32 |
@@ -107,34 +134,13 @@ static inline void ds_set(unsigned char *base, enum ds_qualifier qual, | |||
107 | (*(unsigned long *)base) = value; | 134 | (*(unsigned long *)base) = value; |
108 | } | 135 | } |
109 | 136 | ||
137 | #define DS_ALIGNMENT (1 << 3) /* BTS and PEBS buffer alignment */ | ||
110 | 138 | ||
111 | /* | ||
112 | * Locking is done only for allocating BTS or PEBS resources and for | ||
113 | * guarding context and buffer memory allocation. | ||
114 | * | ||
115 | * Most functions require the current task to own the ds context part | ||
116 | * they are going to access. All the locking is done when validating | ||
117 | * access to the context. | ||
118 | */ | ||
119 | static spinlock_t ds_lock = __SPIN_LOCK_UNLOCKED(ds_lock); | ||
120 | 139 | ||
121 | /* | 140 | /* |
122 | * Validate that the current task is allowed to access the BTS/PEBS | 141 | * Locking is done only for allocating BTS or PEBS resources. |
123 | * buffer of the parameter task. | ||
124 | * | ||
125 | * Returns 0, if access is granted; -Eerrno, otherwise. | ||
126 | */ | 142 | */ |
127 | static inline int ds_validate_access(struct ds_context *context, | 143 | static spinlock_t ds_lock = __SPIN_LOCK_UNLOCKED(ds_lock); |
128 | enum ds_qualifier qual) | ||
129 | { | ||
130 | if (!context) | ||
131 | return -EPERM; | ||
132 | |||
133 | if (context->owner[qual] == current) | ||
134 | return 0; | ||
135 | |||
136 | return -EPERM; | ||
137 | } | ||
138 | 144 | ||
139 | 145 | ||
140 | /* | 146 | /* |
@@ -183,51 +189,13 @@ static inline int check_tracer(struct task_struct *task) | |||
183 | * | 189 | * |
184 | * Contexts are use-counted. They are allocated on first access and | 190 | * Contexts are use-counted. They are allocated on first access and |
185 | * deallocated when the last user puts the context. | 191 | * deallocated when the last user puts the context. |
186 | * | ||
187 | * We distinguish between an allocating and a non-allocating get of a | ||
188 | * context: | ||
189 | * - the allocating get is used for requesting BTS/PEBS resources. It | ||
190 | * requires the caller to hold the global ds_lock. | ||
191 | * - the non-allocating get is used for all other cases. A | ||
192 | * non-existing context indicates an error. It acquires and releases | ||
193 | * the ds_lock itself for obtaining the context. | ||
194 | * | ||
195 | * A context and its DS configuration are allocated and deallocated | ||
196 | * together. A context always has a DS configuration of the | ||
197 | * appropriate size. | ||
198 | */ | 192 | */ |
199 | static DEFINE_PER_CPU(struct ds_context *, system_context); | 193 | static DEFINE_PER_CPU(struct ds_context *, system_context); |
200 | 194 | ||
201 | #define this_system_context per_cpu(system_context, smp_processor_id()) | 195 | #define this_system_context per_cpu(system_context, smp_processor_id()) |
202 | 196 | ||
203 | /* | ||
204 | * Returns the pointer to the parameter task's context or to the | ||
205 | * system-wide context, if task is NULL. | ||
206 | * | ||
207 | * Increases the use count of the returned context, if not NULL. | ||
208 | */ | ||
209 | static inline struct ds_context *ds_get_context(struct task_struct *task) | 197 | static inline struct ds_context *ds_get_context(struct task_struct *task) |
210 | { | 198 | { |
211 | struct ds_context *context; | ||
212 | unsigned long irq; | ||
213 | |||
214 | spin_lock_irqsave(&ds_lock, irq); | ||
215 | |||
216 | context = (task ? task->thread.ds_ctx : this_system_context); | ||
217 | if (context) | ||
218 | context->count++; | ||
219 | |||
220 | spin_unlock_irqrestore(&ds_lock, irq); | ||
221 | |||
222 | return context; | ||
223 | } | ||
224 | |||
225 | /* | ||
226 | * Same as ds_get_context, but allocates the context and it's DS | ||
227 | * structure, if necessary; returns NULL; if out of memory. | ||
228 | */ | ||
229 | static inline struct ds_context *ds_alloc_context(struct task_struct *task) | ||
230 | { | ||
231 | struct ds_context **p_context = | 199 | struct ds_context **p_context = |
232 | (task ? &task->thread.ds_ctx : &this_system_context); | 200 | (task ? &task->thread.ds_ctx : &this_system_context); |
233 | struct ds_context *context = *p_context; | 201 | struct ds_context *context = *p_context; |
@@ -238,16 +206,9 @@ static inline struct ds_context *ds_alloc_context(struct task_struct *task) | |||
238 | if (!context) | 206 | if (!context) |
239 | return NULL; | 207 | return NULL; |
240 | 208 | ||
241 | context->ds = kzalloc(ds_cfg.sizeof_ds, GFP_KERNEL); | ||
242 | if (!context->ds) { | ||
243 | kfree(context); | ||
244 | return NULL; | ||
245 | } | ||
246 | |||
247 | spin_lock_irqsave(&ds_lock, irq); | 209 | spin_lock_irqsave(&ds_lock, irq); |
248 | 210 | ||
249 | if (*p_context) { | 211 | if (*p_context) { |
250 | kfree(context->ds); | ||
251 | kfree(context); | 212 | kfree(context); |
252 | 213 | ||
253 | context = *p_context; | 214 | context = *p_context; |
@@ -272,10 +233,6 @@ static inline struct ds_context *ds_alloc_context(struct task_struct *task) | |||
272 | return context; | 233 | return context; |
273 | } | 234 | } |
274 | 235 | ||
275 | /* | ||
276 | * Decreases the use count of the parameter context, if not NULL. | ||
277 | * Deallocates the context, if the use count reaches zero. | ||
278 | */ | ||
279 | static inline void ds_put_context(struct ds_context *context) | 236 | static inline void ds_put_context(struct ds_context *context) |
280 | { | 237 | { |
281 | unsigned long irq; | 238 | unsigned long irq; |
@@ -296,13 +253,6 @@ static inline void ds_put_context(struct ds_context *context) | |||
296 | if (!context->task || (context->task == current)) | 253 | if (!context->task || (context->task == current)) |
297 | wrmsrl(MSR_IA32_DS_AREA, 0); | 254 | wrmsrl(MSR_IA32_DS_AREA, 0); |
298 | 255 | ||
299 | put_tracer(context->task); | ||
300 | |||
301 | /* free any leftover buffers from tracers that did not | ||
302 | * deallocate them properly. */ | ||
303 | kfree(context->buffer[ds_bts]); | ||
304 | kfree(context->buffer[ds_pebs]); | ||
305 | kfree(context->ds); | ||
306 | kfree(context); | 256 | kfree(context); |
307 | out: | 257 | out: |
308 | spin_unlock_irqrestore(&ds_lock, irq); | 258 | spin_unlock_irqrestore(&ds_lock, irq); |
@@ -312,345 +262,342 @@ static inline void ds_put_context(struct ds_context *context) | |||
312 | /* | 262 | /* |
313 | * Handle a buffer overflow | 263 | * Handle a buffer overflow |
314 | * | 264 | * |
315 | * task: the task whose buffers are overflowing; | ||
316 | * NULL for a buffer overflow on the current cpu | ||
317 | * context: the ds context | 265 | * context: the ds context |
318 | * qual: the buffer type | 266 | * qual: the buffer type |
319 | */ | 267 | */ |
320 | static void ds_overflow(struct task_struct *task, struct ds_context *context, | 268 | static void ds_overflow(struct ds_context *context, enum ds_qualifier qual) |
321 | enum ds_qualifier qual) | 269 | { |
322 | { | 270 | switch (qual) { |
323 | if (!context) | 271 | case ds_bts: { |
324 | return; | 272 | struct bts_tracer *tracer = |
325 | 273 | container_of(context->owner[qual], | |
326 | if (context->callback[qual]) | 274 | struct bts_tracer, ds); |
327 | (*context->callback[qual])(task); | 275 | if (tracer->ovfl) |
328 | 276 | tracer->ovfl(tracer); | |
329 | /* todo: do some more overflow handling */ | 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 | } | ||
330 | } | 288 | } |
331 | 289 | ||
332 | 290 | ||
333 | /* | 291 | static void ds_install_ds_config(struct ds_context *context, |
334 | * Allocate a non-pageable buffer of the parameter size. | 292 | enum ds_qualifier qual, |
335 | * Checks the memory and the locked memory rlimit. | 293 | void *base, size_t size, size_t ith) |
336 | * | ||
337 | * Returns the buffer, if successful; | ||
338 | * NULL, if out of memory or rlimit exceeded. | ||
339 | * | ||
340 | * size: the requested buffer size in bytes | ||
341 | * pages (out): if not NULL, contains the number of pages reserved | ||
342 | */ | ||
343 | static inline void *ds_allocate_buffer(size_t size, unsigned int *pages) | ||
344 | { | 294 | { |
345 | unsigned long rlim, vm, pgsz; | 295 | unsigned long buffer, adj; |
346 | void *buffer; | ||
347 | |||
348 | pgsz = PAGE_ALIGN(size) >> PAGE_SHIFT; | ||
349 | |||
350 | rlim = current->signal->rlim[RLIMIT_AS].rlim_cur >> PAGE_SHIFT; | ||
351 | vm = current->mm->total_vm + pgsz; | ||
352 | if (rlim < vm) | ||
353 | return NULL; | ||
354 | 296 | ||
355 | rlim = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur >> PAGE_SHIFT; | 297 | /* adjust the buffer address and size to meet alignment |
356 | vm = current->mm->locked_vm + pgsz; | 298 | * constraints: |
357 | if (rlim < vm) | 299 | * - buffer is double-word aligned |
358 | return NULL; | 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; | ||
359 | 306 | ||
360 | buffer = kzalloc(size, GFP_KERNEL); | 307 | adj = ALIGN(buffer, DS_ALIGNMENT) - buffer; |
361 | if (!buffer) | 308 | buffer += adj; |
362 | return NULL; | 309 | size -= adj; |
363 | 310 | ||
364 | current->mm->total_vm += pgsz; | 311 | size /= ds_cfg.sizeof_rec[qual]; |
365 | current->mm->locked_vm += pgsz; | 312 | size *= ds_cfg.sizeof_rec[qual]; |
366 | 313 | ||
367 | if (pages) | 314 | ds_set(context->ds, qual, ds_buffer_base, buffer); |
368 | *pages = pgsz; | 315 | ds_set(context->ds, qual, ds_index, buffer); |
316 | ds_set(context->ds, qual, ds_absolute_maximum, buffer + size); | ||
369 | 317 | ||
370 | return buffer; | 318 | /* 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); | ||
371 | } | 323 | } |
372 | 324 | ||
373 | static int ds_request(struct task_struct *task, void *base, size_t size, | 325 | static int ds_request(struct ds_tracer *tracer, enum ds_qualifier qual, |
374 | ds_ovfl_callback_t ovfl, enum ds_qualifier qual) | 326 | struct task_struct *task, |
327 | void *base, size_t size, size_t th) | ||
375 | { | 328 | { |
376 | struct ds_context *context; | 329 | struct ds_context *context; |
377 | unsigned long buffer, adj; | ||
378 | const unsigned long alignment = (1 << 3); | ||
379 | unsigned long irq; | 330 | unsigned long irq; |
380 | int error = 0; | 331 | int error; |
381 | 332 | ||
333 | error = -EOPNOTSUPP; | ||
382 | if (!ds_cfg.sizeof_ds) | 334 | if (!ds_cfg.sizeof_ds) |
383 | return -EOPNOTSUPP; | 335 | goto out; |
336 | |||
337 | error = -EINVAL; | ||
338 | if (!base) | ||
339 | goto out; | ||
384 | 340 | ||
385 | /* we require some space to do alignment adjustments below */ | 341 | /* we require some space to do alignment adjustments below */ |
386 | if (size < (alignment + ds_cfg.sizeof_rec[qual])) | 342 | error = -EINVAL; |
387 | return -EINVAL; | 343 | if (size < (DS_ALIGNMENT + ds_cfg.sizeof_rec[qual])) |
344 | goto out; | ||
388 | 345 | ||
389 | /* buffer overflow notification is not yet implemented */ | 346 | if (th != (size_t)-1) { |
390 | if (ovfl) | 347 | th *= ds_cfg.sizeof_rec[qual]; |
391 | return -EOPNOTSUPP; | 348 | |
349 | error = -EINVAL; | ||
350 | if (size <= th) | ||
351 | goto out; | ||
352 | } | ||
392 | 353 | ||
354 | tracer->buffer = base; | ||
355 | tracer->size = size; | ||
393 | 356 | ||
394 | context = ds_alloc_context(task); | 357 | error = -ENOMEM; |
358 | context = ds_get_context(task); | ||
395 | if (!context) | 359 | if (!context) |
396 | return -ENOMEM; | 360 | goto out; |
361 | tracer->context = context; | ||
362 | |||
397 | 363 | ||
398 | spin_lock_irqsave(&ds_lock, irq); | 364 | spin_lock_irqsave(&ds_lock, irq); |
399 | 365 | ||
400 | error = -EPERM; | 366 | error = -EPERM; |
401 | if (!check_tracer(task)) | 367 | if (!check_tracer(task)) |
402 | goto out_unlock; | 368 | goto out_unlock; |
403 | |||
404 | get_tracer(task); | 369 | get_tracer(task); |
405 | 370 | ||
406 | error = -EALREADY; | ||
407 | if (context->owner[qual] == current) | ||
408 | goto out_put_tracer; | ||
409 | error = -EPERM; | 371 | error = -EPERM; |
410 | if (context->owner[qual] != NULL) | 372 | if (context->owner[qual]) |
411 | goto out_put_tracer; | 373 | goto out_put_tracer; |
412 | context->owner[qual] = current; | 374 | context->owner[qual] = tracer; |
413 | 375 | ||
414 | spin_unlock_irqrestore(&ds_lock, irq); | 376 | spin_unlock_irqrestore(&ds_lock, irq); |
415 | 377 | ||
416 | 378 | ||
417 | error = -ENOMEM; | 379 | ds_install_ds_config(context, qual, base, size, th); |
418 | if (!base) { | ||
419 | base = ds_allocate_buffer(size, &context->pages[qual]); | ||
420 | if (!base) | ||
421 | goto out_release; | ||
422 | |||
423 | context->buffer[qual] = base; | ||
424 | } | ||
425 | error = 0; | ||
426 | 380 | ||
427 | context->callback[qual] = ovfl; | 381 | return 0; |
428 | |||
429 | /* adjust the buffer address and size to meet alignment | ||
430 | * constraints: | ||
431 | * - buffer is double-word aligned | ||
432 | * - size is multiple of record size | ||
433 | * | ||
434 | * We checked the size at the very beginning; we have enough | ||
435 | * space to do the adjustment. | ||
436 | */ | ||
437 | buffer = (unsigned long)base; | ||
438 | |||
439 | adj = ALIGN(buffer, alignment) - buffer; | ||
440 | buffer += adj; | ||
441 | size -= adj; | ||
442 | |||
443 | size /= ds_cfg.sizeof_rec[qual]; | ||
444 | size *= ds_cfg.sizeof_rec[qual]; | ||
445 | |||
446 | ds_set(context->ds, qual, ds_buffer_base, buffer); | ||
447 | ds_set(context->ds, qual, ds_index, buffer); | ||
448 | ds_set(context->ds, qual, ds_absolute_maximum, buffer + size); | ||
449 | |||
450 | if (ovfl) { | ||
451 | /* todo: select a suitable interrupt threshold */ | ||
452 | } else | ||
453 | ds_set(context->ds, qual, | ||
454 | ds_interrupt_threshold, buffer + size + 1); | ||
455 | |||
456 | /* we keep the context until ds_release */ | ||
457 | return error; | ||
458 | |||
459 | out_release: | ||
460 | context->owner[qual] = NULL; | ||
461 | ds_put_context(context); | ||
462 | put_tracer(task); | ||
463 | return error; | ||
464 | 382 | ||
465 | out_put_tracer: | 383 | out_put_tracer: |
466 | spin_unlock_irqrestore(&ds_lock, irq); | ||
467 | ds_put_context(context); | ||
468 | put_tracer(task); | 384 | put_tracer(task); |
469 | return error; | ||
470 | |||
471 | out_unlock: | 385 | out_unlock: |
472 | spin_unlock_irqrestore(&ds_lock, irq); | 386 | spin_unlock_irqrestore(&ds_lock, irq); |
473 | ds_put_context(context); | 387 | ds_put_context(context); |
388 | tracer->context = NULL; | ||
389 | out: | ||
474 | return error; | 390 | return error; |
475 | } | 391 | } |
476 | 392 | ||
477 | int ds_request_bts(struct task_struct *task, void *base, size_t size, | 393 | struct bts_tracer *ds_request_bts(struct task_struct *task, |
478 | ds_ovfl_callback_t ovfl) | 394 | void *base, size_t size, |
395 | bts_ovfl_callback_t ovfl, size_t th) | ||
479 | { | 396 | { |
480 | return ds_request(task, base, size, ovfl, ds_bts); | 397 | struct bts_tracer *tracer; |
481 | } | 398 | int error; |
482 | 399 | ||
483 | int ds_request_pebs(struct task_struct *task, void *base, size_t size, | 400 | /* buffer overflow notification is not yet implemented */ |
484 | ds_ovfl_callback_t ovfl) | 401 | error = -EOPNOTSUPP; |
485 | { | 402 | if (ovfl) |
486 | return ds_request(task, base, size, ovfl, ds_pebs); | 403 | goto out; |
404 | |||
405 | 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: | ||
418 | kfree(tracer); | ||
419 | out: | ||
420 | return ERR_PTR(error); | ||
487 | } | 421 | } |
488 | 422 | ||
489 | static int ds_release(struct task_struct *task, enum ds_qualifier qual) | 423 | struct pebs_tracer *ds_request_pebs(struct task_struct *task, |
424 | void *base, size_t size, | ||
425 | pebs_ovfl_callback_t ovfl, size_t th) | ||
490 | { | 426 | { |
491 | struct ds_context *context; | 427 | struct pebs_tracer *tracer; |
492 | int error; | 428 | int error; |
493 | 429 | ||
494 | context = ds_get_context(task); | 430 | /* buffer overflow notification is not yet implemented */ |
495 | error = ds_validate_access(context, qual); | 431 | error = -EOPNOTSUPP; |
496 | if (error < 0) | 432 | if (ovfl) |
497 | goto out; | 433 | goto out; |
498 | 434 | ||
499 | kfree(context->buffer[qual]); | 435 | error = -ENOMEM; |
500 | context->buffer[qual] = NULL; | 436 | tracer = kzalloc(sizeof(*tracer), GFP_KERNEL); |
437 | if (!tracer) | ||
438 | goto out; | ||
439 | tracer->ovfl = ovfl; | ||
501 | 440 | ||
502 | current->mm->total_vm -= context->pages[qual]; | 441 | error = ds_request(&tracer->ds, ds_pebs, task, base, size, th); |
503 | current->mm->locked_vm -= context->pages[qual]; | 442 | if (error < 0) |
504 | context->pages[qual] = 0; | 443 | goto out_tracer; |
505 | context->owner[qual] = NULL; | ||
506 | 444 | ||
507 | /* | 445 | return tracer; |
508 | * we put the context twice: | 446 | |
509 | * once for the ds_get_context | 447 | out_tracer: |
510 | * once for the corresponding ds_request | 448 | kfree(tracer); |
511 | */ | ||
512 | ds_put_context(context); | ||
513 | out: | 449 | out: |
514 | ds_put_context(context); | 450 | return ERR_PTR(error); |
515 | return error; | ||
516 | } | 451 | } |
517 | 452 | ||
518 | int ds_release_bts(struct task_struct *task) | 453 | static void ds_release(struct ds_tracer *tracer, enum ds_qualifier qual) |
519 | { | 454 | { |
520 | return ds_release(task, ds_bts); | 455 | BUG_ON(tracer->context->owner[qual] != tracer); |
456 | tracer->context->owner[qual] = NULL; | ||
457 | |||
458 | put_tracer(tracer->context->task); | ||
459 | ds_put_context(tracer->context); | ||
521 | } | 460 | } |
522 | 461 | ||
523 | int ds_release_pebs(struct task_struct *task) | 462 | int ds_release_bts(struct bts_tracer *tracer) |
524 | { | 463 | { |
525 | return ds_release(task, ds_pebs); | 464 | if (!tracer) |
465 | return -EINVAL; | ||
466 | |||
467 | ds_release(&tracer->ds, ds_bts); | ||
468 | kfree(tracer); | ||
469 | |||
470 | return 0; | ||
526 | } | 471 | } |
527 | 472 | ||
528 | static int ds_get_index(struct task_struct *task, size_t *pos, | 473 | int ds_release_pebs(struct pebs_tracer *tracer) |
529 | enum ds_qualifier qual) | ||
530 | { | 474 | { |
531 | struct ds_context *context; | 475 | if (!tracer) |
532 | unsigned long base, index; | 476 | return -EINVAL; |
533 | int error; | ||
534 | 477 | ||
535 | context = ds_get_context(task); | 478 | ds_release(&tracer->ds, ds_pebs); |
536 | error = ds_validate_access(context, qual); | 479 | kfree(tracer); |
537 | if (error < 0) | 480 | |
538 | goto out; | 481 | return 0; |
482 | } | ||
483 | |||
484 | static size_t ds_get_index(struct ds_context *context, enum ds_qualifier qual) | ||
485 | { | ||
486 | unsigned long base, index; | ||
539 | 487 | ||
540 | base = ds_get(context->ds, qual, ds_buffer_base); | 488 | base = ds_get(context->ds, qual, ds_buffer_base); |
541 | index = ds_get(context->ds, qual, ds_index); | 489 | index = ds_get(context->ds, qual, ds_index); |
542 | 490 | ||
543 | error = ((index - base) / ds_cfg.sizeof_rec[qual]); | 491 | return (index - base) / ds_cfg.sizeof_rec[qual]; |
544 | if (pos) | ||
545 | *pos = error; | ||
546 | out: | ||
547 | ds_put_context(context); | ||
548 | return error; | ||
549 | } | 492 | } |
550 | 493 | ||
551 | int ds_get_bts_index(struct task_struct *task, size_t *pos) | 494 | int ds_get_bts_index(struct bts_tracer *tracer, size_t *pos) |
552 | { | 495 | { |
553 | return ds_get_index(task, pos, ds_bts); | 496 | 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; | ||
554 | } | 505 | } |
555 | 506 | ||
556 | int ds_get_pebs_index(struct task_struct *task, size_t *pos) | 507 | int ds_get_pebs_index(struct pebs_tracer *tracer, size_t *pos) |
557 | { | 508 | { |
558 | return ds_get_index(task, pos, ds_pebs); | 509 | 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; | ||
559 | } | 518 | } |
560 | 519 | ||
561 | static int ds_get_end(struct task_struct *task, size_t *pos, | 520 | static size_t ds_get_end(struct ds_context *context, enum ds_qualifier qual) |
562 | enum ds_qualifier qual) | ||
563 | { | 521 | { |
564 | struct ds_context *context; | 522 | unsigned long base, max; |
565 | unsigned long base, end; | ||
566 | int error; | ||
567 | |||
568 | context = ds_get_context(task); | ||
569 | error = ds_validate_access(context, qual); | ||
570 | if (error < 0) | ||
571 | goto out; | ||
572 | 523 | ||
573 | base = ds_get(context->ds, qual, ds_buffer_base); | 524 | base = ds_get(context->ds, qual, ds_buffer_base); |
574 | end = ds_get(context->ds, qual, ds_absolute_maximum); | 525 | max = ds_get(context->ds, qual, ds_absolute_maximum); |
575 | 526 | ||
576 | error = ((end - base) / ds_cfg.sizeof_rec[qual]); | 527 | return (max - base) / ds_cfg.sizeof_rec[qual]; |
577 | if (pos) | ||
578 | *pos = error; | ||
579 | out: | ||
580 | ds_put_context(context); | ||
581 | return error; | ||
582 | } | 528 | } |
583 | 529 | ||
584 | int ds_get_bts_end(struct task_struct *task, size_t *pos) | 530 | int ds_get_bts_end(struct bts_tracer *tracer, size_t *pos) |
585 | { | 531 | { |
586 | return ds_get_end(task, pos, ds_bts); | 532 | 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; | ||
587 | } | 541 | } |
588 | 542 | ||
589 | int ds_get_pebs_end(struct task_struct *task, size_t *pos) | 543 | int ds_get_pebs_end(struct pebs_tracer *tracer, size_t *pos) |
590 | { | 544 | { |
591 | return ds_get_end(task, pos, ds_pebs); | 545 | 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; | ||
592 | } | 554 | } |
593 | 555 | ||
594 | static int ds_access(struct task_struct *task, size_t index, | 556 | static int ds_access(struct ds_context *context, enum ds_qualifier qual, |
595 | const void **record, enum ds_qualifier qual) | 557 | size_t index, const void **record) |
596 | { | 558 | { |
597 | struct ds_context *context; | ||
598 | unsigned long base, idx; | 559 | unsigned long base, idx; |
599 | int error; | ||
600 | 560 | ||
601 | if (!record) | 561 | if (!record) |
602 | return -EINVAL; | 562 | return -EINVAL; |
603 | 563 | ||
604 | context = ds_get_context(task); | ||
605 | error = ds_validate_access(context, qual); | ||
606 | if (error < 0) | ||
607 | goto out; | ||
608 | |||
609 | base = ds_get(context->ds, qual, ds_buffer_base); | 564 | base = ds_get(context->ds, qual, ds_buffer_base); |
610 | idx = base + (index * ds_cfg.sizeof_rec[qual]); | 565 | idx = base + (index * ds_cfg.sizeof_rec[qual]); |
611 | 566 | ||
612 | error = -EINVAL; | ||
613 | if (idx > ds_get(context->ds, qual, ds_absolute_maximum)) | 567 | if (idx > ds_get(context->ds, qual, ds_absolute_maximum)) |
614 | goto out; | 568 | return -EINVAL; |
615 | 569 | ||
616 | *record = (const void *)idx; | 570 | *record = (const void *)idx; |
617 | error = ds_cfg.sizeof_rec[qual]; | 571 | |
618 | out: | 572 | return ds_cfg.sizeof_rec[qual]; |
619 | ds_put_context(context); | ||
620 | return error; | ||
621 | } | 573 | } |
622 | 574 | ||
623 | int ds_access_bts(struct task_struct *task, size_t index, const void **record) | 575 | int ds_access_bts(struct bts_tracer *tracer, size_t index, |
576 | const void **record) | ||
624 | { | 577 | { |
625 | return ds_access(task, index, record, ds_bts); | 578 | if (!tracer) |
579 | return -EINVAL; | ||
580 | |||
581 | return ds_access(tracer->ds.context, ds_bts, index, record); | ||
626 | } | 582 | } |
627 | 583 | ||
628 | int ds_access_pebs(struct task_struct *task, size_t index, const void **record) | 584 | int ds_access_pebs(struct pebs_tracer *tracer, size_t index, |
585 | const void **record) | ||
629 | { | 586 | { |
630 | return ds_access(task, index, record, ds_pebs); | 587 | if (!tracer) |
588 | return -EINVAL; | ||
589 | |||
590 | return ds_access(tracer->ds.context, ds_pebs, index, record); | ||
631 | } | 591 | } |
632 | 592 | ||
633 | static int ds_write(struct task_struct *task, const void *record, size_t size, | 593 | static int ds_write(struct ds_context *context, enum ds_qualifier qual, |
634 | enum ds_qualifier qual, int force) | 594 | const void *record, size_t size) |
635 | { | 595 | { |
636 | struct ds_context *context; | 596 | int bytes_written = 0; |
637 | int error; | ||
638 | 597 | ||
639 | if (!record) | 598 | if (!record) |
640 | return -EINVAL; | 599 | return -EINVAL; |
641 | 600 | ||
642 | error = -EPERM; | ||
643 | context = ds_get_context(task); | ||
644 | if (!context) | ||
645 | goto out; | ||
646 | |||
647 | if (!force) { | ||
648 | error = ds_validate_access(context, qual); | ||
649 | if (error < 0) | ||
650 | goto out; | ||
651 | } | ||
652 | |||
653 | error = 0; | ||
654 | while (size) { | 601 | while (size) { |
655 | unsigned long base, index, end, write_end, int_th; | 602 | unsigned long base, index, end, write_end, int_th; |
656 | unsigned long write_size, adj_write_size; | 603 | unsigned long write_size, adj_write_size; |
@@ -678,14 +625,14 @@ static int ds_write(struct task_struct *task, const void *record, size_t size, | |||
678 | write_end = end; | 625 | write_end = end; |
679 | 626 | ||
680 | if (write_end <= index) | 627 | if (write_end <= index) |
681 | goto out; | 628 | break; |
682 | 629 | ||
683 | write_size = min((unsigned long) size, write_end - index); | 630 | write_size = min((unsigned long) size, write_end - index); |
684 | memcpy((void *)index, record, write_size); | 631 | memcpy((void *)index, record, write_size); |
685 | 632 | ||
686 | record = (const char *)record + write_size; | 633 | record = (const char *)record + write_size; |
687 | size -= write_size; | 634 | size -= write_size; |
688 | error += write_size; | 635 | bytes_written += write_size; |
689 | 636 | ||
690 | adj_write_size = write_size / ds_cfg.sizeof_rec[qual]; | 637 | adj_write_size = write_size / ds_cfg.sizeof_rec[qual]; |
691 | adj_write_size *= ds_cfg.sizeof_rec[qual]; | 638 | adj_write_size *= ds_cfg.sizeof_rec[qual]; |
@@ -700,47 +647,32 @@ static int ds_write(struct task_struct *task, const void *record, size_t size, | |||
700 | ds_set(context->ds, qual, ds_index, index); | 647 | ds_set(context->ds, qual, ds_index, index); |
701 | 648 | ||
702 | if (index >= int_th) | 649 | if (index >= int_th) |
703 | ds_overflow(task, context, qual); | 650 | ds_overflow(context, qual); |
704 | } | 651 | } |
705 | 652 | ||
706 | out: | 653 | return bytes_written; |
707 | ds_put_context(context); | ||
708 | return error; | ||
709 | } | 654 | } |
710 | 655 | ||
711 | int ds_write_bts(struct task_struct *task, const void *record, size_t size) | 656 | int ds_write_bts(struct bts_tracer *tracer, const void *record, size_t size) |
712 | { | 657 | { |
713 | return ds_write(task, record, size, ds_bts, /* force = */ 0); | 658 | if (!tracer) |
714 | } | 659 | return -EINVAL; |
715 | 660 | ||
716 | int ds_write_pebs(struct task_struct *task, const void *record, size_t size) | 661 | return ds_write(tracer->ds.context, ds_bts, record, size); |
717 | { | ||
718 | return ds_write(task, record, size, ds_pebs, /* force = */ 0); | ||
719 | } | 662 | } |
720 | 663 | ||
721 | int ds_unchecked_write_bts(struct task_struct *task, | 664 | int ds_write_pebs(struct pebs_tracer *tracer, const void *record, size_t size) |
722 | const void *record, size_t size) | ||
723 | { | 665 | { |
724 | return ds_write(task, record, size, ds_bts, /* force = */ 1); | 666 | if (!tracer) |
725 | } | 667 | return -EINVAL; |
726 | 668 | ||
727 | int ds_unchecked_write_pebs(struct task_struct *task, | 669 | return ds_write(tracer->ds.context, ds_pebs, record, size); |
728 | const void *record, size_t size) | ||
729 | { | ||
730 | return ds_write(task, record, size, ds_pebs, /* force = */ 1); | ||
731 | } | 670 | } |
732 | 671 | ||
733 | static int ds_reset_or_clear(struct task_struct *task, | 672 | static void ds_reset_or_clear(struct ds_context *context, |
734 | enum ds_qualifier qual, int clear) | 673 | enum ds_qualifier qual, int clear) |
735 | { | 674 | { |
736 | struct ds_context *context; | ||
737 | unsigned long base, end; | 675 | unsigned long base, end; |
738 | int error; | ||
739 | |||
740 | context = ds_get_context(task); | ||
741 | error = ds_validate_access(context, qual); | ||
742 | if (error < 0) | ||
743 | goto out; | ||
744 | 676 | ||
745 | base = ds_get(context->ds, qual, ds_buffer_base); | 677 | base = ds_get(context->ds, qual, ds_buffer_base); |
746 | end = ds_get(context->ds, qual, ds_absolute_maximum); | 678 | end = ds_get(context->ds, qual, ds_absolute_maximum); |
@@ -749,70 +681,69 @@ static int ds_reset_or_clear(struct task_struct *task, | |||
749 | memset((void *)base, 0, end - base); | 681 | memset((void *)base, 0, end - base); |
750 | 682 | ||
751 | ds_set(context->ds, qual, ds_index, base); | 683 | ds_set(context->ds, qual, ds_index, base); |
752 | |||
753 | error = 0; | ||
754 | out: | ||
755 | ds_put_context(context); | ||
756 | return error; | ||
757 | } | 684 | } |
758 | 685 | ||
759 | int ds_reset_bts(struct task_struct *task) | 686 | int ds_reset_bts(struct bts_tracer *tracer) |
760 | { | 687 | { |
761 | return ds_reset_or_clear(task, ds_bts, /* clear = */ 0); | 688 | if (!tracer) |
689 | return -EINVAL; | ||
690 | |||
691 | ds_reset_or_clear(tracer->ds.context, ds_bts, /* clear = */ 0); | ||
692 | |||
693 | return 0; | ||
762 | } | 694 | } |
763 | 695 | ||
764 | int ds_reset_pebs(struct task_struct *task) | 696 | int ds_reset_pebs(struct pebs_tracer *tracer) |
765 | { | 697 | { |
766 | return ds_reset_or_clear(task, ds_pebs, /* clear = */ 0); | 698 | if (!tracer) |
699 | return -EINVAL; | ||
700 | |||
701 | ds_reset_or_clear(tracer->ds.context, ds_pebs, /* clear = */ 0); | ||
702 | |||
703 | return 0; | ||
767 | } | 704 | } |
768 | 705 | ||
769 | int ds_clear_bts(struct task_struct *task) | 706 | int ds_clear_bts(struct bts_tracer *tracer) |
770 | { | 707 | { |
771 | return ds_reset_or_clear(task, ds_bts, /* clear = */ 1); | 708 | if (!tracer) |
709 | return -EINVAL; | ||
710 | |||
711 | ds_reset_or_clear(tracer->ds.context, ds_bts, /* clear = */ 1); | ||
712 | |||
713 | return 0; | ||
772 | } | 714 | } |
773 | 715 | ||
774 | int ds_clear_pebs(struct task_struct *task) | 716 | int ds_clear_pebs(struct pebs_tracer *tracer) |
775 | { | 717 | { |
776 | return ds_reset_or_clear(task, ds_pebs, /* clear = */ 1); | 718 | if (!tracer) |
719 | return -EINVAL; | ||
720 | |||
721 | ds_reset_or_clear(tracer->ds.context, ds_pebs, /* clear = */ 1); | ||
722 | |||
723 | return 0; | ||
777 | } | 724 | } |
778 | 725 | ||
779 | int ds_get_pebs_reset(struct task_struct *task, u64 *value) | 726 | int ds_get_pebs_reset(struct pebs_tracer *tracer, u64 *value) |
780 | { | 727 | { |
781 | struct ds_context *context; | 728 | if (!tracer) |
782 | int error; | 729 | return -EINVAL; |
783 | 730 | ||
784 | if (!value) | 731 | if (!value) |
785 | return -EINVAL; | 732 | return -EINVAL; |
786 | 733 | ||
787 | context = ds_get_context(task); | 734 | *value = *(u64 *)(tracer->ds.context->ds + (ds_cfg.sizeof_field * 8)); |
788 | error = ds_validate_access(context, ds_pebs); | ||
789 | if (error < 0) | ||
790 | goto out; | ||
791 | 735 | ||
792 | *value = *(u64 *)(context->ds + (ds_cfg.sizeof_field * 8)); | 736 | return 0; |
793 | |||
794 | error = 0; | ||
795 | out: | ||
796 | ds_put_context(context); | ||
797 | return error; | ||
798 | } | 737 | } |
799 | 738 | ||
800 | int ds_set_pebs_reset(struct task_struct *task, u64 value) | 739 | int ds_set_pebs_reset(struct pebs_tracer *tracer, u64 value) |
801 | { | 740 | { |
802 | struct ds_context *context; | 741 | if (!tracer) |
803 | int error; | 742 | return -EINVAL; |
804 | |||
805 | context = ds_get_context(task); | ||
806 | error = ds_validate_access(context, ds_pebs); | ||
807 | if (error < 0) | ||
808 | goto out; | ||
809 | 743 | ||
810 | *(u64 *)(context->ds + (ds_cfg.sizeof_field * 8)) = value; | 744 | *(u64 *)(tracer->ds.context->ds + (ds_cfg.sizeof_field * 8)) = value; |
811 | 745 | ||
812 | error = 0; | 746 | return 0; |
813 | out: | ||
814 | ds_put_context(context); | ||
815 | return error; | ||
816 | } | 747 | } |
817 | 748 | ||
818 | static const struct ds_configuration ds_cfg_var = { | 749 | static const struct ds_configuration ds_cfg_var = { |
@@ -840,6 +771,10 @@ static inline void | |||
840 | ds_configure(const struct ds_configuration *cfg) | 771 | ds_configure(const struct ds_configuration *cfg) |
841 | { | 772 | { |
842 | ds_cfg = *cfg; | 773 | ds_cfg = *cfg; |
774 | |||
775 | printk(KERN_INFO "DS available\n"); | ||
776 | |||
777 | BUG_ON(MAX_SIZEOF_DS < ds_cfg.sizeof_ds); | ||
843 | } | 778 | } |
844 | 779 | ||
845 | void __cpuinit ds_init_intel(struct cpuinfo_x86 *c) | 780 | void __cpuinit ds_init_intel(struct cpuinfo_x86 *c) |
@@ -847,17 +782,16 @@ void __cpuinit ds_init_intel(struct cpuinfo_x86 *c) | |||
847 | switch (c->x86) { | 782 | switch (c->x86) { |
848 | case 0x6: | 783 | case 0x6: |
849 | switch (c->x86_model) { | 784 | switch (c->x86_model) { |
785 | case 0 ... 0xC: | ||
786 | /* sorry, don't know about them */ | ||
787 | break; | ||
850 | case 0xD: | 788 | case 0xD: |
851 | case 0xE: /* Pentium M */ | 789 | case 0xE: /* Pentium M */ |
852 | ds_configure(&ds_cfg_var); | 790 | ds_configure(&ds_cfg_var); |
853 | break; | 791 | break; |
854 | case 0xF: /* Core2 */ | 792 | default: /* Core2, Atom, ... */ |
855 | case 0x1C: /* Atom */ | ||
856 | ds_configure(&ds_cfg_64); | 793 | ds_configure(&ds_cfg_64); |
857 | break; | 794 | break; |
858 | default: | ||
859 | /* sorry, don't know about them */ | ||
860 | break; | ||
861 | } | 795 | } |
862 | break; | 796 | break; |
863 | case 0xF: | 797 | case 0xF: |
@@ -884,6 +818,8 @@ void ds_free(struct ds_context *context) | |||
884 | * is dying. There should not be any user of that context left | 818 | * is dying. There should not be any user of that context left |
885 | * to disturb us, anymore. */ | 819 | * to disturb us, anymore. */ |
886 | unsigned long leftovers = context->count; | 820 | unsigned long leftovers = context->count; |
887 | while (leftovers--) | 821 | while (leftovers--) { |
822 | put_tracer(context->task); | ||
888 | ds_put_context(context); | 823 | ds_put_context(context); |
824 | } | ||
889 | } | 825 | } |