diff options
Diffstat (limited to 'kernel/perf_event.c')
-rw-r--r-- | kernel/perf_event.c | 89 |
1 files changed, 57 insertions, 32 deletions
diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 9ecaa45ab6b2..3256e36ad251 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c | |||
@@ -2674,20 +2674,21 @@ static void perf_output_wakeup(struct perf_output_handle *handle) | |||
2674 | static void perf_output_lock(struct perf_output_handle *handle) | 2674 | static void perf_output_lock(struct perf_output_handle *handle) |
2675 | { | 2675 | { |
2676 | struct perf_mmap_data *data = handle->data; | 2676 | struct perf_mmap_data *data = handle->data; |
2677 | int cpu; | 2677 | int cur, cpu = get_cpu(); |
2678 | 2678 | ||
2679 | handle->locked = 0; | 2679 | handle->locked = 0; |
2680 | 2680 | ||
2681 | local_irq_save(handle->flags); | 2681 | for (;;) { |
2682 | cpu = smp_processor_id(); | 2682 | cur = atomic_cmpxchg(&data->lock, -1, cpu); |
2683 | 2683 | if (cur == -1) { | |
2684 | if (in_nmi() && atomic_read(&data->lock) == cpu) | 2684 | handle->locked = 1; |
2685 | return; | 2685 | break; |
2686 | } | ||
2687 | if (cur == cpu) | ||
2688 | break; | ||
2686 | 2689 | ||
2687 | while (atomic_cmpxchg(&data->lock, -1, cpu) != -1) | ||
2688 | cpu_relax(); | 2690 | cpu_relax(); |
2689 | 2691 | } | |
2690 | handle->locked = 1; | ||
2691 | } | 2692 | } |
2692 | 2693 | ||
2693 | static void perf_output_unlock(struct perf_output_handle *handle) | 2694 | static void perf_output_unlock(struct perf_output_handle *handle) |
@@ -2733,7 +2734,7 @@ again: | |||
2733 | if (atomic_xchg(&data->wakeup, 0)) | 2734 | if (atomic_xchg(&data->wakeup, 0)) |
2734 | perf_output_wakeup(handle); | 2735 | perf_output_wakeup(handle); |
2735 | out: | 2736 | out: |
2736 | local_irq_restore(handle->flags); | 2737 | put_cpu(); |
2737 | } | 2738 | } |
2738 | 2739 | ||
2739 | void perf_output_copy(struct perf_output_handle *handle, | 2740 | void perf_output_copy(struct perf_output_handle *handle, |
@@ -3976,8 +3977,9 @@ static enum hrtimer_restart perf_swevent_hrtimer(struct hrtimer *hrtimer) | |||
3976 | regs = task_pt_regs(current); | 3977 | regs = task_pt_regs(current); |
3977 | 3978 | ||
3978 | if (regs) { | 3979 | if (regs) { |
3979 | if (perf_event_overflow(event, 0, &data, regs)) | 3980 | if (!(event->attr.exclude_idle && current->pid == 0)) |
3980 | ret = HRTIMER_NORESTART; | 3981 | if (perf_event_overflow(event, 0, &data, regs)) |
3982 | ret = HRTIMER_NORESTART; | ||
3981 | } | 3983 | } |
3982 | 3984 | ||
3983 | period = max_t(u64, 10000, event->hw.sample_period); | 3985 | period = max_t(u64, 10000, event->hw.sample_period); |
@@ -3986,6 +3988,42 @@ static enum hrtimer_restart perf_swevent_hrtimer(struct hrtimer *hrtimer) | |||
3986 | return ret; | 3988 | return ret; |
3987 | } | 3989 | } |
3988 | 3990 | ||
3991 | static void perf_swevent_start_hrtimer(struct perf_event *event) | ||
3992 | { | ||
3993 | struct hw_perf_event *hwc = &event->hw; | ||
3994 | |||
3995 | hrtimer_init(&hwc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | ||
3996 | hwc->hrtimer.function = perf_swevent_hrtimer; | ||
3997 | if (hwc->sample_period) { | ||
3998 | u64 period; | ||
3999 | |||
4000 | if (hwc->remaining) { | ||
4001 | if (hwc->remaining < 0) | ||
4002 | period = 10000; | ||
4003 | else | ||
4004 | period = hwc->remaining; | ||
4005 | hwc->remaining = 0; | ||
4006 | } else { | ||
4007 | period = max_t(u64, 10000, hwc->sample_period); | ||
4008 | } | ||
4009 | __hrtimer_start_range_ns(&hwc->hrtimer, | ||
4010 | ns_to_ktime(period), 0, | ||
4011 | HRTIMER_MODE_REL, 0); | ||
4012 | } | ||
4013 | } | ||
4014 | |||
4015 | static void perf_swevent_cancel_hrtimer(struct perf_event *event) | ||
4016 | { | ||
4017 | struct hw_perf_event *hwc = &event->hw; | ||
4018 | |||
4019 | if (hwc->sample_period) { | ||
4020 | ktime_t remaining = hrtimer_get_remaining(&hwc->hrtimer); | ||
4021 | hwc->remaining = ktime_to_ns(remaining); | ||
4022 | |||
4023 | hrtimer_cancel(&hwc->hrtimer); | ||
4024 | } | ||
4025 | } | ||
4026 | |||
3989 | /* | 4027 | /* |
3990 | * Software event: cpu wall time clock | 4028 | * Software event: cpu wall time clock |
3991 | */ | 4029 | */ |
@@ -4008,22 +4046,14 @@ static int cpu_clock_perf_event_enable(struct perf_event *event) | |||
4008 | int cpu = raw_smp_processor_id(); | 4046 | int cpu = raw_smp_processor_id(); |
4009 | 4047 | ||
4010 | atomic64_set(&hwc->prev_count, cpu_clock(cpu)); | 4048 | atomic64_set(&hwc->prev_count, cpu_clock(cpu)); |
4011 | hrtimer_init(&hwc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | 4049 | perf_swevent_start_hrtimer(event); |
4012 | hwc->hrtimer.function = perf_swevent_hrtimer; | ||
4013 | if (hwc->sample_period) { | ||
4014 | u64 period = max_t(u64, 10000, hwc->sample_period); | ||
4015 | __hrtimer_start_range_ns(&hwc->hrtimer, | ||
4016 | ns_to_ktime(period), 0, | ||
4017 | HRTIMER_MODE_REL, 0); | ||
4018 | } | ||
4019 | 4050 | ||
4020 | return 0; | 4051 | return 0; |
4021 | } | 4052 | } |
4022 | 4053 | ||
4023 | static void cpu_clock_perf_event_disable(struct perf_event *event) | 4054 | static void cpu_clock_perf_event_disable(struct perf_event *event) |
4024 | { | 4055 | { |
4025 | if (event->hw.sample_period) | 4056 | perf_swevent_cancel_hrtimer(event); |
4026 | hrtimer_cancel(&event->hw.hrtimer); | ||
4027 | cpu_clock_perf_event_update(event); | 4057 | cpu_clock_perf_event_update(event); |
4028 | } | 4058 | } |
4029 | 4059 | ||
@@ -4060,22 +4090,15 @@ static int task_clock_perf_event_enable(struct perf_event *event) | |||
4060 | now = event->ctx->time; | 4090 | now = event->ctx->time; |
4061 | 4091 | ||
4062 | atomic64_set(&hwc->prev_count, now); | 4092 | atomic64_set(&hwc->prev_count, now); |
4063 | hrtimer_init(&hwc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | 4093 | |
4064 | hwc->hrtimer.function = perf_swevent_hrtimer; | 4094 | perf_swevent_start_hrtimer(event); |
4065 | if (hwc->sample_period) { | ||
4066 | u64 period = max_t(u64, 10000, hwc->sample_period); | ||
4067 | __hrtimer_start_range_ns(&hwc->hrtimer, | ||
4068 | ns_to_ktime(period), 0, | ||
4069 | HRTIMER_MODE_REL, 0); | ||
4070 | } | ||
4071 | 4095 | ||
4072 | return 0; | 4096 | return 0; |
4073 | } | 4097 | } |
4074 | 4098 | ||
4075 | static void task_clock_perf_event_disable(struct perf_event *event) | 4099 | static void task_clock_perf_event_disable(struct perf_event *event) |
4076 | { | 4100 | { |
4077 | if (event->hw.sample_period) | 4101 | perf_swevent_cancel_hrtimer(event); |
4078 | hrtimer_cancel(&event->hw.hrtimer); | ||
4079 | task_clock_perf_event_update(event, event->ctx->time); | 4102 | task_clock_perf_event_update(event, event->ctx->time); |
4080 | 4103 | ||
4081 | } | 4104 | } |
@@ -4252,6 +4275,8 @@ static const struct pmu *sw_perf_event_init(struct perf_event *event) | |||
4252 | case PERF_COUNT_SW_PAGE_FAULTS_MAJ: | 4275 | case PERF_COUNT_SW_PAGE_FAULTS_MAJ: |
4253 | case PERF_COUNT_SW_CONTEXT_SWITCHES: | 4276 | case PERF_COUNT_SW_CONTEXT_SWITCHES: |
4254 | case PERF_COUNT_SW_CPU_MIGRATIONS: | 4277 | case PERF_COUNT_SW_CPU_MIGRATIONS: |
4278 | case PERF_COUNT_SW_ALIGNMENT_FAULTS: | ||
4279 | case PERF_COUNT_SW_EMULATION_FAULTS: | ||
4255 | if (!event->parent) { | 4280 | if (!event->parent) { |
4256 | atomic_inc(&perf_swevent_enabled[event_id]); | 4281 | atomic_inc(&perf_swevent_enabled[event_id]); |
4257 | event->destroy = sw_perf_event_destroy; | 4282 | event->destroy = sw_perf_event_destroy; |