diff options
| -rw-r--r-- | arch/x86/Kconfig | 2 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 20 | ||||
| -rw-r--r-- | include/linux/perf_event.h | 30 | ||||
| -rw-r--r-- | kernel/perf_event.c | 24 |
4 files changed, 57 insertions, 19 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index e8327686d3c5..e330da21b84f 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
| @@ -21,7 +21,7 @@ config X86 | |||
| 21 | select HAVE_UNSTABLE_SCHED_CLOCK | 21 | select HAVE_UNSTABLE_SCHED_CLOCK |
| 22 | select HAVE_IDE | 22 | select HAVE_IDE |
| 23 | select HAVE_OPROFILE | 23 | select HAVE_OPROFILE |
| 24 | select HAVE_PERF_EVENTS if (!M386 && !M486) | 24 | select HAVE_PERF_EVENTS |
| 25 | select HAVE_IRQ_WORK | 25 | select HAVE_IRQ_WORK |
| 26 | select HAVE_IOREMAP_PROT | 26 | select HAVE_IOREMAP_PROT |
| 27 | select HAVE_KPROBES | 27 | select HAVE_KPROBES |
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index ed6310183efb..6d75b9145b13 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
| @@ -381,6 +381,20 @@ static void release_pmc_hardware(void) {} | |||
| 381 | 381 | ||
| 382 | #endif | 382 | #endif |
| 383 | 383 | ||
| 384 | static bool check_hw_exists(void) | ||
| 385 | { | ||
| 386 | u64 val, val_new = 0; | ||
| 387 | int ret = 0; | ||
| 388 | |||
| 389 | val = 0xabcdUL; | ||
| 390 | ret |= checking_wrmsrl(x86_pmu.perfctr, val); | ||
| 391 | ret |= rdmsrl_safe(x86_pmu.perfctr, &val_new); | ||
| 392 | if (ret || val != val_new) | ||
| 393 | return false; | ||
| 394 | |||
| 395 | return true; | ||
| 396 | } | ||
| 397 | |||
| 384 | static void reserve_ds_buffers(void); | 398 | static void reserve_ds_buffers(void); |
| 385 | static void release_ds_buffers(void); | 399 | static void release_ds_buffers(void); |
| 386 | 400 | ||
| @@ -1372,6 +1386,12 @@ void __init init_hw_perf_events(void) | |||
| 1372 | 1386 | ||
| 1373 | pmu_check_apic(); | 1387 | pmu_check_apic(); |
| 1374 | 1388 | ||
| 1389 | /* sanity check that the hardware exists or is emulated */ | ||
| 1390 | if (!check_hw_exists()) { | ||
| 1391 | pr_cont("Broken PMU hardware detected, software events only.\n"); | ||
| 1392 | return; | ||
| 1393 | } | ||
| 1394 | |||
| 1375 | pr_cont("%s PMU driver.\n", x86_pmu.name); | 1395 | pr_cont("%s PMU driver.\n", x86_pmu.name); |
| 1376 | 1396 | ||
| 1377 | if (x86_pmu.quirks) | 1397 | if (x86_pmu.quirks) |
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 40150f345982..de2c41758e29 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
| @@ -850,6 +850,7 @@ struct perf_event_context { | |||
| 850 | int nr_active; | 850 | int nr_active; |
| 851 | int is_active; | 851 | int is_active; |
| 852 | int nr_stat; | 852 | int nr_stat; |
| 853 | int rotate_disable; | ||
| 853 | atomic_t refcount; | 854 | atomic_t refcount; |
| 854 | struct task_struct *task; | 855 | struct task_struct *task; |
| 855 | 856 | ||
| @@ -908,20 +909,6 @@ extern int perf_num_counters(void); | |||
| 908 | extern const char *perf_pmu_name(void); | 909 | extern const char *perf_pmu_name(void); |
| 909 | extern void __perf_event_task_sched_in(struct task_struct *task); | 910 | extern void __perf_event_task_sched_in(struct task_struct *task); |
| 910 | extern void __perf_event_task_sched_out(struct task_struct *task, struct task_struct *next); | 911 | extern void __perf_event_task_sched_out(struct task_struct *task, struct task_struct *next); |
| 911 | |||
| 912 | extern atomic_t perf_task_events; | ||
| 913 | |||
| 914 | static inline void perf_event_task_sched_in(struct task_struct *task) | ||
| 915 | { | ||
| 916 | COND_STMT(&perf_task_events, __perf_event_task_sched_in(task)); | ||
| 917 | } | ||
| 918 | |||
| 919 | static inline | ||
| 920 | void perf_event_task_sched_out(struct task_struct *task, struct task_struct *next) | ||
| 921 | { | ||
| 922 | COND_STMT(&perf_task_events, __perf_event_task_sched_out(task, next)); | ||
| 923 | } | ||
| 924 | |||
| 925 | extern int perf_event_init_task(struct task_struct *child); | 912 | extern int perf_event_init_task(struct task_struct *child); |
| 926 | extern void perf_event_exit_task(struct task_struct *child); | 913 | extern void perf_event_exit_task(struct task_struct *child); |
| 927 | extern void perf_event_free_task(struct task_struct *task); | 914 | extern void perf_event_free_task(struct task_struct *task); |
| @@ -1030,6 +1017,21 @@ have_event: | |||
| 1030 | __perf_sw_event(event_id, nr, nmi, regs, addr); | 1017 | __perf_sw_event(event_id, nr, nmi, regs, addr); |
| 1031 | } | 1018 | } |
| 1032 | 1019 | ||
| 1020 | extern atomic_t perf_task_events; | ||
| 1021 | |||
| 1022 | static inline void perf_event_task_sched_in(struct task_struct *task) | ||
| 1023 | { | ||
| 1024 | COND_STMT(&perf_task_events, __perf_event_task_sched_in(task)); | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | static inline | ||
| 1028 | void perf_event_task_sched_out(struct task_struct *task, struct task_struct *next) | ||
| 1029 | { | ||
| 1030 | perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 1, NULL, 0); | ||
| 1031 | |||
| 1032 | COND_STMT(&perf_task_events, __perf_event_task_sched_out(task, next)); | ||
| 1033 | } | ||
| 1034 | |||
| 1033 | extern void perf_event_mmap(struct vm_area_struct *vma); | 1035 | extern void perf_event_mmap(struct vm_area_struct *vma); |
| 1034 | extern struct perf_guest_info_callbacks *perf_guest_cbs; | 1036 | extern struct perf_guest_info_callbacks *perf_guest_cbs; |
| 1035 | extern int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks); | 1037 | extern int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks); |
diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 671f6c8c8a32..eac7e3364335 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c | |||
| @@ -1287,8 +1287,6 @@ void __perf_event_task_sched_out(struct task_struct *task, | |||
| 1287 | { | 1287 | { |
| 1288 | int ctxn; | 1288 | int ctxn; |
| 1289 | 1289 | ||
| 1290 | perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 1, NULL, 0); | ||
| 1291 | |||
| 1292 | for_each_task_context_nr(ctxn) | 1290 | for_each_task_context_nr(ctxn) |
| 1293 | perf_event_context_sched_out(task, ctxn, next); | 1291 | perf_event_context_sched_out(task, ctxn, next); |
| 1294 | } | 1292 | } |
| @@ -1622,8 +1620,12 @@ static void rotate_ctx(struct perf_event_context *ctx) | |||
| 1622 | { | 1620 | { |
| 1623 | raw_spin_lock(&ctx->lock); | 1621 | raw_spin_lock(&ctx->lock); |
| 1624 | 1622 | ||
| 1625 | /* Rotate the first entry last of non-pinned groups */ | 1623 | /* |
| 1626 | list_rotate_left(&ctx->flexible_groups); | 1624 | * Rotate the first entry last of non-pinned groups. Rotation might be |
| 1625 | * disabled by the inheritance code. | ||
| 1626 | */ | ||
| 1627 | if (!ctx->rotate_disable) | ||
| 1628 | list_rotate_left(&ctx->flexible_groups); | ||
| 1627 | 1629 | ||
| 1628 | raw_spin_unlock(&ctx->lock); | 1630 | raw_spin_unlock(&ctx->lock); |
| 1629 | } | 1631 | } |
| @@ -6162,6 +6164,7 @@ int perf_event_init_context(struct task_struct *child, int ctxn) | |||
| 6162 | struct perf_event *event; | 6164 | struct perf_event *event; |
| 6163 | struct task_struct *parent = current; | 6165 | struct task_struct *parent = current; |
| 6164 | int inherited_all = 1; | 6166 | int inherited_all = 1; |
| 6167 | unsigned long flags; | ||
| 6165 | int ret = 0; | 6168 | int ret = 0; |
| 6166 | 6169 | ||
| 6167 | child->perf_event_ctxp[ctxn] = NULL; | 6170 | child->perf_event_ctxp[ctxn] = NULL; |
| @@ -6202,6 +6205,15 @@ int perf_event_init_context(struct task_struct *child, int ctxn) | |||
| 6202 | break; | 6205 | break; |
| 6203 | } | 6206 | } |
| 6204 | 6207 | ||
| 6208 | /* | ||
| 6209 | * We can't hold ctx->lock when iterating the ->flexible_group list due | ||
| 6210 | * to allocations, but we need to prevent rotation because | ||
| 6211 | * rotate_ctx() will change the list from interrupt context. | ||
| 6212 | */ | ||
| 6213 | raw_spin_lock_irqsave(&parent_ctx->lock, flags); | ||
| 6214 | parent_ctx->rotate_disable = 1; | ||
| 6215 | raw_spin_unlock_irqrestore(&parent_ctx->lock, flags); | ||
| 6216 | |||
| 6205 | list_for_each_entry(event, &parent_ctx->flexible_groups, group_entry) { | 6217 | list_for_each_entry(event, &parent_ctx->flexible_groups, group_entry) { |
| 6206 | ret = inherit_task_group(event, parent, parent_ctx, | 6218 | ret = inherit_task_group(event, parent, parent_ctx, |
| 6207 | child, ctxn, &inherited_all); | 6219 | child, ctxn, &inherited_all); |
| @@ -6209,6 +6221,10 @@ int perf_event_init_context(struct task_struct *child, int ctxn) | |||
| 6209 | break; | 6221 | break; |
| 6210 | } | 6222 | } |
| 6211 | 6223 | ||
| 6224 | raw_spin_lock_irqsave(&parent_ctx->lock, flags); | ||
| 6225 | parent_ctx->rotate_disable = 0; | ||
| 6226 | raw_spin_unlock_irqrestore(&parent_ctx->lock, flags); | ||
| 6227 | |||
| 6212 | child_ctx = child->perf_event_ctxp[ctxn]; | 6228 | child_ctx = child->perf_event_ctxp[ctxn]; |
| 6213 | 6229 | ||
| 6214 | if (child_ctx && inherited_all) { | 6230 | if (child_ctx && inherited_all) { |
