diff options
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/include/asm/ds.h | 12 | ||||
-rw-r--r-- | arch/x86/kernel/ds.c | 92 | ||||
-rw-r--r-- | arch/x86/kernel/ptrace.c | 22 |
3 files changed, 38 insertions, 88 deletions
diff --git a/arch/x86/include/asm/ds.h b/arch/x86/include/asm/ds.h index 0af997de5f01..99b6c39774a4 100644 --- a/arch/x86/include/asm/ds.h +++ b/arch/x86/include/asm/ds.h | |||
@@ -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. |
@@ -54,8 +53,7 @@ typedef void (*pebs_ovfl_callback_t)(struct pebs_tracer *); | |||
54 | * task: the task to request recording for; | 53 | * task: the task to request recording for; |
55 | * NULL for per-cpu recording on the current cpu | 54 | * NULL for per-cpu recording on the current cpu |
56 | * base: the base pointer for the (non-pageable) buffer; | 55 | * base: the base pointer for the (non-pageable) buffer; |
57 | * NULL if buffer allocation requested | 56 | * size: the size of the provided buffer in bytes |
58 | * size: the size of the requested or provided buffer in bytes | ||
59 | * ovfl: pointer to a function to be called on buffer overflow; | 57 | * ovfl: pointer to a function to be called on buffer overflow; |
60 | * NULL if cyclic buffer requested | 58 | * NULL if cyclic buffer requested |
61 | * th: the interrupt threshold in records from the end of the buffer; | 59 | * th: the interrupt threshold in records from the end of the buffer; |
@@ -72,8 +70,6 @@ extern struct pebs_tracer *ds_request_pebs(struct task_struct *task, | |||
72 | /* | 70 | /* |
73 | * Release BTS or PEBS resources | 71 | * Release BTS or PEBS resources |
74 | * | 72 | * |
75 | * Frees buffers allocated on ds_request. | ||
76 | * | ||
77 | * Returns 0 on success; -Eerrno otherwise | 73 | * Returns 0 on success; -Eerrno otherwise |
78 | * | 74 | * |
79 | * tracer: the tracer handle returned from ds_request_~() | 75 | * tracer: the tracer handle returned from ds_request_~() |
diff --git a/arch/x86/kernel/ds.c b/arch/x86/kernel/ds.c index 96768e9cce99..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. |
@@ -57,8 +56,6 @@ struct ds_tracer { | |||
57 | /* the buffer provided on ds_request() and its size in bytes */ | 56 | /* the buffer provided on ds_request() and its size in bytes */ |
58 | void *buffer; | 57 | void *buffer; |
59 | size_t size; | 58 | size_t size; |
60 | /* the number of allocated pages for on-request allocated buffers */ | ||
61 | unsigned int pages; | ||
62 | }; | 59 | }; |
63 | 60 | ||
64 | struct bts_tracer { | 61 | struct bts_tracer { |
@@ -141,8 +138,7 @@ static inline void ds_set(unsigned char *base, enum ds_qualifier qual, | |||
141 | 138 | ||
142 | 139 | ||
143 | /* | 140 | /* |
144 | * Locking is done only for allocating BTS or PEBS resources and for | 141 | * Locking is done only for allocating BTS or PEBS resources. |
145 | * guarding context and buffer memory allocation. | ||
146 | */ | 142 | */ |
147 | static spinlock_t ds_lock = __SPIN_LOCK_UNLOCKED(ds_lock); | 143 | static spinlock_t ds_lock = __SPIN_LOCK_UNLOCKED(ds_lock); |
148 | 144 | ||
@@ -292,50 +288,6 @@ static void ds_overflow(struct ds_context *context, enum ds_qualifier qual) | |||
292 | } | 288 | } |
293 | 289 | ||
294 | 290 | ||
295 | /* | ||
296 | * Allocate a non-pageable buffer of the parameter size. | ||
297 | * Checks the memory and the locked memory rlimit. | ||
298 | * | ||
299 | * Returns the buffer, if successful; | ||
300 | * NULL, if out of memory or rlimit exceeded. | ||
301 | * | ||
302 | * size: the requested buffer size in bytes | ||
303 | * pages (out): if not NULL, contains the number of pages reserved | ||
304 | */ | ||
305 | static inline void *ds_allocate_buffer(size_t size, unsigned int *pages) | ||
306 | { | ||
307 | unsigned long rlim, vm, pgsz; | ||
308 | void *buffer = NULL; | ||
309 | |||
310 | pgsz = PAGE_ALIGN(size) >> PAGE_SHIFT; | ||
311 | |||
312 | down_write(¤t->mm->mmap_sem); | ||
313 | |||
314 | rlim = current->signal->rlim[RLIMIT_AS].rlim_cur >> PAGE_SHIFT; | ||
315 | vm = current->mm->total_vm + pgsz; | ||
316 | if (rlim < vm) | ||
317 | goto out; | ||
318 | |||
319 | rlim = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur >> PAGE_SHIFT; | ||
320 | vm = current->mm->locked_vm + pgsz; | ||
321 | if (rlim < vm) | ||
322 | goto out; | ||
323 | |||
324 | buffer = kzalloc(size, GFP_KERNEL); | ||
325 | if (!buffer) | ||
326 | goto out; | ||
327 | |||
328 | current->mm->total_vm += pgsz; | ||
329 | current->mm->locked_vm += pgsz; | ||
330 | |||
331 | if (pages) | ||
332 | *pages = pgsz; | ||
333 | |||
334 | out: | ||
335 | up_write(¤t->mm->mmap_sem); | ||
336 | return buffer; | ||
337 | } | ||
338 | |||
339 | static void ds_install_ds_config(struct ds_context *context, | 291 | static void ds_install_ds_config(struct ds_context *context, |
340 | enum ds_qualifier qual, | 292 | enum ds_qualifier qual, |
341 | void *base, size_t size, size_t ith) | 293 | void *base, size_t size, size_t ith) |
@@ -382,6 +334,10 @@ static int ds_request(struct ds_tracer *tracer, enum ds_qualifier qual, | |||
382 | if (!ds_cfg.sizeof_ds) | 334 | if (!ds_cfg.sizeof_ds) |
383 | goto out; | 335 | goto out; |
384 | 336 | ||
337 | error = -EINVAL; | ||
338 | if (!base) | ||
339 | goto out; | ||
340 | |||
385 | /* we require some space to do alignment adjustments below */ | 341 | /* we require some space to do alignment adjustments below */ |
386 | error = -EINVAL; | 342 | error = -EINVAL; |
387 | if (size < (DS_ALIGNMENT + ds_cfg.sizeof_rec[qual])) | 343 | if (size < (DS_ALIGNMENT + ds_cfg.sizeof_rec[qual])) |
@@ -395,13 +351,6 @@ static int ds_request(struct ds_tracer *tracer, enum ds_qualifier qual, | |||
395 | goto out; | 351 | goto out; |
396 | } | 352 | } |
397 | 353 | ||
398 | error = -ENOMEM; | ||
399 | if (!base) { | ||
400 | base = ds_allocate_buffer(size, &tracer->pages); | ||
401 | if (!base) | ||
402 | goto out; | ||
403 | } | ||
404 | |||
405 | tracer->buffer = base; | 354 | tracer->buffer = base; |
406 | tracer->size = size; | 355 | tracer->size = size; |
407 | 356 | ||
@@ -466,7 +415,7 @@ struct bts_tracer *ds_request_bts(struct task_struct *task, | |||
466 | return tracer; | 415 | return tracer; |
467 | 416 | ||
468 | out_tracer: | 417 | out_tracer: |
469 | (void)ds_release_bts(tracer); | 418 | kfree(tracer); |
470 | out: | 419 | out: |
471 | return ERR_PTR(error); | 420 | return ERR_PTR(error); |
472 | } | 421 | } |
@@ -496,31 +445,18 @@ struct pebs_tracer *ds_request_pebs(struct task_struct *task, | |||
496 | return tracer; | 445 | return tracer; |
497 | 446 | ||
498 | out_tracer: | 447 | out_tracer: |
499 | (void)ds_release_pebs(tracer); | 448 | kfree(tracer); |
500 | out: | 449 | out: |
501 | return ERR_PTR(error); | 450 | return ERR_PTR(error); |
502 | } | 451 | } |
503 | 452 | ||
504 | static void ds_release(struct ds_tracer *tracer, enum ds_qualifier qual) | 453 | static void ds_release(struct ds_tracer *tracer, enum ds_qualifier qual) |
505 | { | 454 | { |
506 | if (tracer->context) { | 455 | BUG_ON(tracer->context->owner[qual] != tracer); |
507 | BUG_ON(tracer->context->owner[qual] != tracer); | 456 | tracer->context->owner[qual] = NULL; |
508 | tracer->context->owner[qual] = NULL; | ||
509 | |||
510 | put_tracer(tracer->context->task); | ||
511 | ds_put_context(tracer->context); | ||
512 | } | ||
513 | 457 | ||
514 | if (tracer->pages) { | 458 | put_tracer(tracer->context->task); |
515 | kfree(tracer->buffer); | 459 | ds_put_context(tracer->context); |
516 | |||
517 | down_write(¤t->mm->mmap_sem); | ||
518 | |||
519 | current->mm->total_vm -= tracer->pages; | ||
520 | current->mm->locked_vm -= tracer->pages; | ||
521 | |||
522 | up_write(¤t->mm->mmap_sem); | ||
523 | } | ||
524 | } | 460 | } |
525 | 461 | ||
526 | int ds_release_bts(struct bts_tracer *tracer) | 462 | int ds_release_bts(struct bts_tracer *tracer) |
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 76adf5b640ff..2c8ec1ba75e6 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -758,6 +758,10 @@ static int ptrace_bts_config(struct task_struct *child, | |||
758 | bts_ovfl_callback_t ovfl = NULL; | 758 | bts_ovfl_callback_t ovfl = NULL; |
759 | unsigned int sig = 0; | 759 | unsigned int sig = 0; |
760 | 760 | ||
761 | error = -EINVAL; | ||
762 | if (cfg.size < (10 * bts_cfg.sizeof_bts)) | ||
763 | goto errout; | ||
764 | |||
761 | if (cfg.flags & PTRACE_BTS_O_SIGNAL) { | 765 | if (cfg.flags & PTRACE_BTS_O_SIGNAL) { |
762 | if (!cfg.signal) | 766 | if (!cfg.signal) |
763 | goto errout; | 767 | goto errout; |
@@ -768,14 +772,26 @@ static int ptrace_bts_config(struct task_struct *child, | |||
768 | sig = cfg.signal; | 772 | sig = cfg.signal; |
769 | } | 773 | } |
770 | 774 | ||
771 | if (child->bts) | 775 | if (child->bts) { |
772 | (void)ds_release_bts(child->bts); | 776 | (void)ds_release_bts(child->bts); |
777 | kfree(child->bts_buffer); | ||
778 | |||
779 | child->bts = NULL; | ||
780 | child->bts_buffer = NULL; | ||
781 | } | ||
782 | |||
783 | error = -ENOMEM; | ||
784 | child->bts_buffer = kzalloc(cfg.size, GFP_KERNEL); | ||
785 | if (!child->bts_buffer) | ||
786 | goto errout; | ||
773 | 787 | ||
774 | child->bts = ds_request_bts(child, /* base = */ NULL, cfg.size, | 788 | child->bts = ds_request_bts(child, child->bts_buffer, cfg.size, |
775 | ovfl, /* th = */ (size_t)-1); | 789 | ovfl, /* th = */ (size_t)-1); |
776 | if (IS_ERR(child->bts)) { | 790 | if (IS_ERR(child->bts)) { |
777 | error = PTR_ERR(child->bts); | 791 | error = PTR_ERR(child->bts); |
792 | kfree(child->bts_buffer); | ||
778 | child->bts = NULL; | 793 | child->bts = NULL; |
794 | child->bts_buffer = NULL; | ||
779 | goto errout; | 795 | goto errout; |
780 | } | 796 | } |
781 | 797 | ||
@@ -972,6 +988,8 @@ void ptrace_disable(struct task_struct *child) | |||
972 | #ifdef CONFIG_X86_PTRACE_BTS | 988 | #ifdef CONFIG_X86_PTRACE_BTS |
973 | if (child->bts) { | 989 | if (child->bts) { |
974 | (void)ds_release_bts(child->bts); | 990 | (void)ds_release_bts(child->bts); |
991 | kfree(child->bts_buffer); | ||
992 | child->bts_buffer = NULL; | ||
975 | 993 | ||
976 | child->thread.debugctlmsr &= ~bts_cfg.debugctl_mask; | 994 | child->thread.debugctlmsr &= ~bts_cfg.debugctl_mask; |
977 | if (!child->thread.debugctlmsr) | 995 | if (!child->thread.debugctlmsr) |