diff options
-rw-r--r-- | include/linux/perf_counter.h | 2 | ||||
-rw-r--r-- | kernel/fork.c | 2 | ||||
-rw-r--r-- | kernel/perf_counter.c | 43 |
3 files changed, 43 insertions, 4 deletions
diff --git a/include/linux/perf_counter.h b/include/linux/perf_counter.h index 717bf3b59ba4..519a41bba249 100644 --- a/include/linux/perf_counter.h +++ b/include/linux/perf_counter.h | |||
@@ -579,6 +579,7 @@ extern void perf_counter_task_sched_out(struct task_struct *task, | |||
579 | extern void perf_counter_task_tick(struct task_struct *task, int cpu); | 579 | extern void perf_counter_task_tick(struct task_struct *task, int cpu); |
580 | extern int perf_counter_init_task(struct task_struct *child); | 580 | extern int perf_counter_init_task(struct task_struct *child); |
581 | extern void perf_counter_exit_task(struct task_struct *child); | 581 | extern void perf_counter_exit_task(struct task_struct *child); |
582 | extern void perf_counter_free_task(struct task_struct *task); | ||
582 | extern void perf_counter_do_pending(void); | 583 | extern void perf_counter_do_pending(void); |
583 | extern void perf_counter_print_debug(void); | 584 | extern void perf_counter_print_debug(void); |
584 | extern void __perf_disable(void); | 585 | extern void __perf_disable(void); |
@@ -644,6 +645,7 @@ static inline void | |||
644 | perf_counter_task_tick(struct task_struct *task, int cpu) { } | 645 | perf_counter_task_tick(struct task_struct *task, int cpu) { } |
645 | static inline int perf_counter_init_task(struct task_struct *child) { return 0; } | 646 | static inline int perf_counter_init_task(struct task_struct *child) { return 0; } |
646 | static inline void perf_counter_exit_task(struct task_struct *child) { } | 647 | static inline void perf_counter_exit_task(struct task_struct *child) { } |
648 | static inline void perf_counter_free_task(struct task_struct *task) { } | ||
647 | static inline void perf_counter_do_pending(void) { } | 649 | static inline void perf_counter_do_pending(void) { } |
648 | static inline void perf_counter_print_debug(void) { } | 650 | static inline void perf_counter_print_debug(void) { } |
649 | static inline void perf_disable(void) { } | 651 | static inline void perf_disable(void) { } |
diff --git a/kernel/fork.c b/kernel/fork.c index c07c3335ceac..23bf757ed321 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -1298,7 +1298,7 @@ bad_fork_cleanup_semundo: | |||
1298 | bad_fork_cleanup_audit: | 1298 | bad_fork_cleanup_audit: |
1299 | audit_free(p); | 1299 | audit_free(p); |
1300 | bad_fork_cleanup_policy: | 1300 | bad_fork_cleanup_policy: |
1301 | perf_counter_exit_task(p); | 1301 | perf_counter_free_task(p); |
1302 | #ifdef CONFIG_NUMA | 1302 | #ifdef CONFIG_NUMA |
1303 | mpol_put(p->mempolicy); | 1303 | mpol_put(p->mempolicy); |
1304 | bad_fork_cleanup_cgroup: | 1304 | bad_fork_cleanup_cgroup: |
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c index 0c000d305e0e..79c3f26541d3 100644 --- a/kernel/perf_counter.c +++ b/kernel/perf_counter.c | |||
@@ -3538,8 +3538,7 @@ static void sync_child_counter(struct perf_counter *child_counter, | |||
3538 | } | 3538 | } |
3539 | 3539 | ||
3540 | static void | 3540 | static void |
3541 | __perf_counter_exit_task(struct task_struct *child, | 3541 | __perf_counter_exit_task(struct perf_counter *child_counter, |
3542 | struct perf_counter *child_counter, | ||
3543 | struct perf_counter_context *child_ctx) | 3542 | struct perf_counter_context *child_ctx) |
3544 | { | 3543 | { |
3545 | struct perf_counter *parent_counter; | 3544 | struct perf_counter *parent_counter; |
@@ -3605,7 +3604,7 @@ void perf_counter_exit_task(struct task_struct *child) | |||
3605 | again: | 3604 | again: |
3606 | list_for_each_entry_safe(child_counter, tmp, &child_ctx->counter_list, | 3605 | list_for_each_entry_safe(child_counter, tmp, &child_ctx->counter_list, |
3607 | list_entry) | 3606 | list_entry) |
3608 | __perf_counter_exit_task(child, child_counter, child_ctx); | 3607 | __perf_counter_exit_task(child_counter, child_ctx); |
3609 | 3608 | ||
3610 | /* | 3609 | /* |
3611 | * If the last counter was a group counter, it will have appended all | 3610 | * If the last counter was a group counter, it will have appended all |
@@ -3621,6 +3620,44 @@ again: | |||
3621 | } | 3620 | } |
3622 | 3621 | ||
3623 | /* | 3622 | /* |
3623 | * free an unexposed, unused context as created by inheritance by | ||
3624 | * init_task below, used by fork() in case of fail. | ||
3625 | */ | ||
3626 | void perf_counter_free_task(struct task_struct *task) | ||
3627 | { | ||
3628 | struct perf_counter_context *ctx = task->perf_counter_ctxp; | ||
3629 | struct perf_counter *counter, *tmp; | ||
3630 | |||
3631 | if (!ctx) | ||
3632 | return; | ||
3633 | |||
3634 | mutex_lock(&ctx->mutex); | ||
3635 | again: | ||
3636 | list_for_each_entry_safe(counter, tmp, &ctx->counter_list, list_entry) { | ||
3637 | struct perf_counter *parent = counter->parent; | ||
3638 | |||
3639 | if (WARN_ON_ONCE(!parent)) | ||
3640 | continue; | ||
3641 | |||
3642 | mutex_lock(&parent->child_mutex); | ||
3643 | list_del_init(&counter->child_list); | ||
3644 | mutex_unlock(&parent->child_mutex); | ||
3645 | |||
3646 | fput(parent->filp); | ||
3647 | |||
3648 | list_del_counter(counter, ctx); | ||
3649 | free_counter(counter); | ||
3650 | } | ||
3651 | |||
3652 | if (!list_empty(&ctx->counter_list)) | ||
3653 | goto again; | ||
3654 | |||
3655 | mutex_unlock(&ctx->mutex); | ||
3656 | |||
3657 | put_ctx(ctx); | ||
3658 | } | ||
3659 | |||
3660 | /* | ||
3624 | * Initialize the perf_counter context in task_struct | 3661 | * Initialize the perf_counter context in task_struct |
3625 | */ | 3662 | */ |
3626 | int perf_counter_init_task(struct task_struct *child) | 3663 | int perf_counter_init_task(struct task_struct *child) |