diff options
author | Ingo Molnar <mingo@elte.hu> | 2008-12-11 08:59:31 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-12-11 09:45:55 -0500 |
commit | 1d1c7ddbfab358445a542715551301b7fc363e28 (patch) | |
tree | d4421834d109b206f39c2019ea039fd42ed22e1d | |
parent | bae43c9945ebeef15e7952e317efb02393d3bfc7 (diff) |
perf counters: add prctl interface to disable/enable counters
Add a way for self-monitoring tasks to disable/enable counters summarily,
via a prctl:
PR_TASK_PERF_COUNTERS_DISABLE 31
PR_TASK_PERF_COUNTERS_ENABLE 32
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | include/linux/perf_counter.h | 4 | ||||
-rw-r--r-- | include/linux/prctl.h | 3 | ||||
-rw-r--r-- | kernel/perf_counter.c | 86 | ||||
-rw-r--r-- | kernel/sys.c | 7 |
4 files changed, 93 insertions, 7 deletions
diff --git a/include/linux/perf_counter.h b/include/linux/perf_counter.h index 30c0ec8c1ee3..97d86c293ee8 100644 --- a/include/linux/perf_counter.h +++ b/include/linux/perf_counter.h | |||
@@ -213,6 +213,8 @@ extern u64 hw_perf_save_disable(void); | |||
213 | extern void hw_perf_restore(u64 ctrl); | 213 | extern void hw_perf_restore(u64 ctrl); |
214 | extern void atomic64_counter_set(struct perf_counter *counter, u64 val64); | 214 | extern void atomic64_counter_set(struct perf_counter *counter, u64 val64); |
215 | extern u64 atomic64_counter_read(struct perf_counter *counter); | 215 | extern u64 atomic64_counter_read(struct perf_counter *counter); |
216 | extern int perf_counter_task_disable(void); | ||
217 | extern int perf_counter_task_enable(void); | ||
216 | 218 | ||
217 | #else | 219 | #else |
218 | static inline void | 220 | static inline void |
@@ -226,6 +228,8 @@ static inline void perf_counter_notify(struct pt_regs *regs) { } | |||
226 | static inline void perf_counter_print_debug(void) { } | 228 | static inline void perf_counter_print_debug(void) { } |
227 | static inline void hw_perf_restore(u64 ctrl) { } | 229 | static inline void hw_perf_restore(u64 ctrl) { } |
228 | static inline u64 hw_perf_save_disable(void) { return 0; } | 230 | static inline u64 hw_perf_save_disable(void) { return 0; } |
231 | static inline int perf_counter_task_disable(void) { return -EINVAL; } | ||
232 | static inline int perf_counter_task_enable(void) { return -EINVAL; } | ||
229 | #endif | 233 | #endif |
230 | 234 | ||
231 | #endif /* _LINUX_PERF_COUNTER_H */ | 235 | #endif /* _LINUX_PERF_COUNTER_H */ |
diff --git a/include/linux/prctl.h b/include/linux/prctl.h index 48d887e3c6e7..b00df4c79c63 100644 --- a/include/linux/prctl.h +++ b/include/linux/prctl.h | |||
@@ -85,4 +85,7 @@ | |||
85 | #define PR_SET_TIMERSLACK 29 | 85 | #define PR_SET_TIMERSLACK 29 |
86 | #define PR_GET_TIMERSLACK 30 | 86 | #define PR_GET_TIMERSLACK 30 |
87 | 87 | ||
88 | #define PR_TASK_PERF_COUNTERS_DISABLE 31 | ||
89 | #define PR_TASK_PERF_COUNTERS_ENABLE 32 | ||
90 | |||
88 | #endif /* _LINUX_PRCTL_H */ | 91 | #endif /* _LINUX_PRCTL_H */ |
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c index a0fe8474ee29..4e679b91d8bb 100644 --- a/kernel/perf_counter.c +++ b/kernel/perf_counter.c | |||
@@ -415,6 +415,9 @@ counter_sched_in(struct perf_counter *counter, | |||
415 | struct perf_counter_context *ctx, | 415 | struct perf_counter_context *ctx, |
416 | int cpu) | 416 | int cpu) |
417 | { | 417 | { |
418 | if (counter->active == -1) | ||
419 | return; | ||
420 | |||
418 | counter->hw_ops->hw_perf_counter_enable(counter); | 421 | counter->hw_ops->hw_perf_counter_enable(counter); |
419 | counter->active = 1; | 422 | counter->active = 1; |
420 | counter->oncpu = cpu; /* TODO: put 'cpu' into cpuctx->cpu */ | 423 | counter->oncpu = cpu; /* TODO: put 'cpu' into cpuctx->cpu */ |
@@ -479,6 +482,79 @@ void perf_counter_task_sched_in(struct task_struct *task, int cpu) | |||
479 | cpuctx->task_ctx = ctx; | 482 | cpuctx->task_ctx = ctx; |
480 | } | 483 | } |
481 | 484 | ||
485 | int perf_counter_task_disable(void) | ||
486 | { | ||
487 | struct task_struct *curr = current; | ||
488 | struct perf_counter_context *ctx = &curr->perf_counter_ctx; | ||
489 | struct perf_counter *counter; | ||
490 | u64 perf_flags; | ||
491 | int cpu; | ||
492 | |||
493 | if (likely(!ctx->nr_counters)) | ||
494 | return 0; | ||
495 | |||
496 | local_irq_disable(); | ||
497 | cpu = smp_processor_id(); | ||
498 | |||
499 | perf_counter_task_sched_out(curr, cpu); | ||
500 | |||
501 | spin_lock(&ctx->lock); | ||
502 | |||
503 | /* | ||
504 | * Disable all the counters: | ||
505 | */ | ||
506 | perf_flags = hw_perf_save_disable(); | ||
507 | |||
508 | list_for_each_entry(counter, &ctx->counter_list, list_entry) { | ||
509 | WARN_ON_ONCE(counter->active == 1); | ||
510 | counter->active = -1; | ||
511 | } | ||
512 | hw_perf_restore(perf_flags); | ||
513 | |||
514 | spin_unlock(&ctx->lock); | ||
515 | |||
516 | local_irq_enable(); | ||
517 | |||
518 | return 0; | ||
519 | } | ||
520 | |||
521 | int perf_counter_task_enable(void) | ||
522 | { | ||
523 | struct task_struct *curr = current; | ||
524 | struct perf_counter_context *ctx = &curr->perf_counter_ctx; | ||
525 | struct perf_counter *counter; | ||
526 | u64 perf_flags; | ||
527 | int cpu; | ||
528 | |||
529 | if (likely(!ctx->nr_counters)) | ||
530 | return 0; | ||
531 | |||
532 | local_irq_disable(); | ||
533 | cpu = smp_processor_id(); | ||
534 | |||
535 | spin_lock(&ctx->lock); | ||
536 | |||
537 | /* | ||
538 | * Disable all the counters: | ||
539 | */ | ||
540 | perf_flags = hw_perf_save_disable(); | ||
541 | |||
542 | list_for_each_entry(counter, &ctx->counter_list, list_entry) { | ||
543 | if (counter->active != -1) | ||
544 | continue; | ||
545 | counter->active = 0; | ||
546 | } | ||
547 | hw_perf_restore(perf_flags); | ||
548 | |||
549 | spin_unlock(&ctx->lock); | ||
550 | |||
551 | perf_counter_task_sched_in(curr, cpu); | ||
552 | |||
553 | local_irq_enable(); | ||
554 | |||
555 | return 0; | ||
556 | } | ||
557 | |||
482 | void perf_counter_task_tick(struct task_struct *curr, int cpu) | 558 | void perf_counter_task_tick(struct task_struct *curr, int cpu) |
483 | { | 559 | { |
484 | struct perf_counter_context *ctx = &curr->perf_counter_ctx; | 560 | struct perf_counter_context *ctx = &curr->perf_counter_ctx; |
@@ -951,13 +1027,9 @@ perf_counter_alloc(struct perf_counter_hw_event *hw_event, | |||
951 | * @cpu: target cpu | 1027 | * @cpu: target cpu |
952 | * @group_fd: group leader counter fd | 1028 | * @group_fd: group leader counter fd |
953 | */ | 1029 | */ |
954 | asmlinkage int sys_perf_counter_open( | 1030 | asmlinkage int |
955 | 1031 | sys_perf_counter_open(struct perf_counter_hw_event *hw_event_uptr __user, | |
956 | struct perf_counter_hw_event *hw_event_uptr __user, | 1032 | pid_t pid, int cpu, int group_fd) |
957 | pid_t pid, | ||
958 | int cpu, | ||
959 | int group_fd) | ||
960 | |||
961 | { | 1033 | { |
962 | struct perf_counter *counter, *group_leader; | 1034 | struct perf_counter *counter, *group_leader; |
963 | struct perf_counter_hw_event hw_event; | 1035 | struct perf_counter_hw_event hw_event; |
diff --git a/kernel/sys.c b/kernel/sys.c index 31deba8f7d16..0f66633be319 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/prctl.h> | 14 | #include <linux/prctl.h> |
15 | #include <linux/highuid.h> | 15 | #include <linux/highuid.h> |
16 | #include <linux/fs.h> | 16 | #include <linux/fs.h> |
17 | #include <linux/perf_counter.h> | ||
17 | #include <linux/resource.h> | 18 | #include <linux/resource.h> |
18 | #include <linux/kernel.h> | 19 | #include <linux/kernel.h> |
19 | #include <linux/kexec.h> | 20 | #include <linux/kexec.h> |
@@ -1716,6 +1717,12 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3, | |||
1716 | case PR_SET_TSC: | 1717 | case PR_SET_TSC: |
1717 | error = SET_TSC_CTL(arg2); | 1718 | error = SET_TSC_CTL(arg2); |
1718 | break; | 1719 | break; |
1720 | case PR_TASK_PERF_COUNTERS_DISABLE: | ||
1721 | error = perf_counter_task_disable(); | ||
1722 | break; | ||
1723 | case PR_TASK_PERF_COUNTERS_ENABLE: | ||
1724 | error = perf_counter_task_enable(); | ||
1725 | break; | ||
1719 | case PR_GET_TIMERSLACK: | 1726 | case PR_GET_TIMERSLACK: |
1720 | error = current->timer_slack_ns; | 1727 | error = current->timer_slack_ns; |
1721 | break; | 1728 | break; |