diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2009-05-25 08:45:27 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-05-25 08:55:01 -0400 |
commit | 6ab423e0eaca827fbd201ca4ae7d4f8573a366b2 (patch) | |
tree | 072d227934bf213adf5c0dd022369e14f273dc48 | |
parent | 771d7cde144d87f2d1fbee4da3c6234d61f7e42a (diff) |
perf_counter: Propagate inheritance failures down the fork() path
Fail fork() when we fail inheritance for some reason (-ENOMEM most likely).
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: John Kacur <jkacur@redhat.com>
LKML-Reference: <20090525124600.324656474@chello.nl>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | include/linux/perf_counter.h | 4 | ||||
-rw-r--r-- | kernel/fork.c | 6 | ||||
-rw-r--r-- | kernel/perf_counter.c | 20 |
3 files changed, 19 insertions, 11 deletions
diff --git a/include/linux/perf_counter.h b/include/linux/perf_counter.h index b1f2bac09f97..d3e85de9bf1e 100644 --- a/include/linux/perf_counter.h +++ b/include/linux/perf_counter.h | |||
@@ -566,7 +566,7 @@ extern void perf_counter_task_sched_in(struct task_struct *task, int cpu); | |||
566 | extern void perf_counter_task_sched_out(struct task_struct *task, | 566 | extern void perf_counter_task_sched_out(struct task_struct *task, |
567 | struct task_struct *next, int cpu); | 567 | struct task_struct *next, int cpu); |
568 | extern void perf_counter_task_tick(struct task_struct *task, int cpu); | 568 | extern void perf_counter_task_tick(struct task_struct *task, int cpu); |
569 | extern void perf_counter_init_task(struct task_struct *child); | 569 | extern int perf_counter_init_task(struct task_struct *child); |
570 | extern void perf_counter_exit_task(struct task_struct *child); | 570 | extern void perf_counter_exit_task(struct task_struct *child); |
571 | extern void perf_counter_do_pending(void); | 571 | extern void perf_counter_do_pending(void); |
572 | extern void perf_counter_print_debug(void); | 572 | extern void perf_counter_print_debug(void); |
@@ -631,7 +631,7 @@ perf_counter_task_sched_out(struct task_struct *task, | |||
631 | struct task_struct *next, int cpu) { } | 631 | struct task_struct *next, int cpu) { } |
632 | static inline void | 632 | static inline void |
633 | perf_counter_task_tick(struct task_struct *task, int cpu) { } | 633 | perf_counter_task_tick(struct task_struct *task, int cpu) { } |
634 | static inline void perf_counter_init_task(struct task_struct *child) { } | 634 | static inline int perf_counter_init_task(struct task_struct *child) { } |
635 | static inline void perf_counter_exit_task(struct task_struct *child) { } | 635 | static inline void perf_counter_exit_task(struct task_struct *child) { } |
636 | static inline void perf_counter_do_pending(void) { } | 636 | static inline void perf_counter_do_pending(void) { } |
637 | static inline void perf_counter_print_debug(void) { } | 637 | static inline void perf_counter_print_debug(void) { } |
diff --git a/kernel/fork.c b/kernel/fork.c index 675e01e9072a..c07c3335ceac 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -1095,7 +1095,10 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
1095 | 1095 | ||
1096 | /* Perform scheduler related setup. Assign this task to a CPU. */ | 1096 | /* Perform scheduler related setup. Assign this task to a CPU. */ |
1097 | sched_fork(p, clone_flags); | 1097 | sched_fork(p, clone_flags); |
1098 | perf_counter_init_task(p); | 1098 | |
1099 | retval = perf_counter_init_task(p); | ||
1100 | if (retval) | ||
1101 | goto bad_fork_cleanup_policy; | ||
1099 | 1102 | ||
1100 | if ((retval = audit_alloc(p))) | 1103 | if ((retval = audit_alloc(p))) |
1101 | goto bad_fork_cleanup_policy; | 1104 | goto bad_fork_cleanup_policy; |
@@ -1295,6 +1298,7 @@ bad_fork_cleanup_semundo: | |||
1295 | bad_fork_cleanup_audit: | 1298 | bad_fork_cleanup_audit: |
1296 | audit_free(p); | 1299 | audit_free(p); |
1297 | bad_fork_cleanup_policy: | 1300 | bad_fork_cleanup_policy: |
1301 | perf_counter_exit_task(p); | ||
1298 | #ifdef CONFIG_NUMA | 1302 | #ifdef CONFIG_NUMA |
1299 | mpol_put(p->mempolicy); | 1303 | mpol_put(p->mempolicy); |
1300 | bad_fork_cleanup_cgroup: | 1304 | bad_fork_cleanup_cgroup: |
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c index 217dbcce2ebd..7a7a144870ef 100644 --- a/kernel/perf_counter.c +++ b/kernel/perf_counter.c | |||
@@ -3434,18 +3434,23 @@ again: | |||
3434 | /* | 3434 | /* |
3435 | * Initialize the perf_counter context in task_struct | 3435 | * Initialize the perf_counter context in task_struct |
3436 | */ | 3436 | */ |
3437 | void perf_counter_init_task(struct task_struct *child) | 3437 | int perf_counter_init_task(struct task_struct *child) |
3438 | { | 3438 | { |
3439 | struct perf_counter_context *child_ctx, *parent_ctx; | 3439 | struct perf_counter_context *child_ctx, *parent_ctx; |
3440 | struct perf_counter *counter; | 3440 | struct perf_counter *counter; |
3441 | struct task_struct *parent = current; | 3441 | struct task_struct *parent = current; |
3442 | int inherited_all = 1; | 3442 | int inherited_all = 1; |
3443 | int ret = 0; | ||
3443 | 3444 | ||
3444 | child->perf_counter_ctxp = NULL; | 3445 | child->perf_counter_ctxp = NULL; |
3445 | 3446 | ||
3446 | mutex_init(&child->perf_counter_mutex); | 3447 | mutex_init(&child->perf_counter_mutex); |
3447 | INIT_LIST_HEAD(&child->perf_counter_list); | 3448 | INIT_LIST_HEAD(&child->perf_counter_list); |
3448 | 3449 | ||
3450 | parent_ctx = parent->perf_counter_ctxp; | ||
3451 | if (likely(!parent_ctx || !parent_ctx->nr_counters)) | ||
3452 | return 0; | ||
3453 | |||
3449 | /* | 3454 | /* |
3450 | * This is executed from the parent task context, so inherit | 3455 | * This is executed from the parent task context, so inherit |
3451 | * counters that have been marked for cloning. | 3456 | * counters that have been marked for cloning. |
@@ -3454,11 +3459,7 @@ void perf_counter_init_task(struct task_struct *child) | |||
3454 | 3459 | ||
3455 | child_ctx = kmalloc(sizeof(struct perf_counter_context), GFP_KERNEL); | 3460 | child_ctx = kmalloc(sizeof(struct perf_counter_context), GFP_KERNEL); |
3456 | if (!child_ctx) | 3461 | if (!child_ctx) |
3457 | return; | 3462 | return -ENOMEM; |
3458 | |||
3459 | parent_ctx = parent->perf_counter_ctxp; | ||
3460 | if (likely(!parent_ctx || !parent_ctx->nr_counters)) | ||
3461 | return; | ||
3462 | 3463 | ||
3463 | __perf_counter_init_context(child_ctx, child); | 3464 | __perf_counter_init_context(child_ctx, child); |
3464 | child->perf_counter_ctxp = child_ctx; | 3465 | child->perf_counter_ctxp = child_ctx; |
@@ -3482,8 +3483,9 @@ void perf_counter_init_task(struct task_struct *child) | |||
3482 | continue; | 3483 | continue; |
3483 | } | 3484 | } |
3484 | 3485 | ||
3485 | if (inherit_group(counter, parent, | 3486 | ret = inherit_group(counter, parent, parent_ctx, |
3486 | parent_ctx, child, child_ctx)) { | 3487 | child, child_ctx); |
3488 | if (ret) { | ||
3487 | inherited_all = 0; | 3489 | inherited_all = 0; |
3488 | break; | 3490 | break; |
3489 | } | 3491 | } |
@@ -3505,6 +3507,8 @@ void perf_counter_init_task(struct task_struct *child) | |||
3505 | } | 3507 | } |
3506 | 3508 | ||
3507 | mutex_unlock(&parent_ctx->mutex); | 3509 | mutex_unlock(&parent_ctx->mutex); |
3510 | |||
3511 | return ret; | ||
3508 | } | 3512 | } |
3509 | 3513 | ||
3510 | static void __cpuinit perf_counter_init_cpu(int cpu) | 3514 | static void __cpuinit perf_counter_init_cpu(int cpu) |