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 b1f2bac09f9..d3e85de9bf1 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 675e01e9072..c07c3335cea 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 217dbcce2eb..7a7a144870e 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) |
