diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-12-05 19:54:00 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-12-05 19:54:00 -0500 |
commit | 232ea344550c4a099d35d9df552509d6748a31c0 (patch) | |
tree | 2b4a1c351d290537fdf18cc5bed78bf43536f175 /kernel | |
parent | 40c043b077c6e377c8440d71563c055d0c4f0f0a (diff) | |
parent | dc440d10e1668b15fe704a23adb9b53fbbb24a44 (diff) |
Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
* 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
perf: Fix loss of notification with multi-event
perf, x86: Force IBS LVT offset assignment for family 10h
perf, x86: Disable PEBS on SandyBridge chips
trace_events_filter: Use rcu_assign_pointer() when setting ftrace_event_call->filter
perf session: Fix crash with invalid CPU list
perf python: Fix undefined symbol problem
perf/x86: Enable raw event access to Intel offcore events
perf: Don't use -ENOSPC for out of PMU resources
perf: Do not set task_ctx pointer in cpuctx if there are no events in the context
perf/x86: Fix PEBS instruction unwind
oprofile, x86: Fix crash when unloading module (nmi timer mode)
oprofile: Fix crash when unloading module (hr timer mode)
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/events/core.c | 89 | ||||
-rw-r--r-- | kernel/events/internal.h | 3 | ||||
-rw-r--r-- | kernel/events/ring_buffer.c | 3 | ||||
-rw-r--r-- | kernel/trace/trace_events_filter.c | 6 |
4 files changed, 95 insertions, 6 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c index 0e8457da6f95..600c1629b64d 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
@@ -185,6 +185,9 @@ static void cpu_ctx_sched_in(struct perf_cpu_context *cpuctx, | |||
185 | static void update_context_time(struct perf_event_context *ctx); | 185 | static void update_context_time(struct perf_event_context *ctx); |
186 | static u64 perf_event_time(struct perf_event *event); | 186 | static u64 perf_event_time(struct perf_event *event); |
187 | 187 | ||
188 | static void ring_buffer_attach(struct perf_event *event, | ||
189 | struct ring_buffer *rb); | ||
190 | |||
188 | void __weak perf_event_print_debug(void) { } | 191 | void __weak perf_event_print_debug(void) { } |
189 | 192 | ||
190 | extern __weak const char *perf_pmu_name(void) | 193 | extern __weak const char *perf_pmu_name(void) |
@@ -2173,7 +2176,8 @@ static void perf_event_context_sched_in(struct perf_event_context *ctx, | |||
2173 | 2176 | ||
2174 | perf_event_sched_in(cpuctx, ctx, task); | 2177 | perf_event_sched_in(cpuctx, ctx, task); |
2175 | 2178 | ||
2176 | cpuctx->task_ctx = ctx; | 2179 | if (ctx->nr_events) |
2180 | cpuctx->task_ctx = ctx; | ||
2177 | 2181 | ||
2178 | perf_pmu_enable(ctx->pmu); | 2182 | perf_pmu_enable(ctx->pmu); |
2179 | perf_ctx_unlock(cpuctx, ctx); | 2183 | perf_ctx_unlock(cpuctx, ctx); |
@@ -3190,12 +3194,33 @@ static unsigned int perf_poll(struct file *file, poll_table *wait) | |||
3190 | struct ring_buffer *rb; | 3194 | struct ring_buffer *rb; |
3191 | unsigned int events = POLL_HUP; | 3195 | unsigned int events = POLL_HUP; |
3192 | 3196 | ||
3197 | /* | ||
3198 | * Race between perf_event_set_output() and perf_poll(): perf_poll() | ||
3199 | * grabs the rb reference but perf_event_set_output() overrides it. | ||
3200 | * Here is the timeline for two threads T1, T2: | ||
3201 | * t0: T1, rb = rcu_dereference(event->rb) | ||
3202 | * t1: T2, old_rb = event->rb | ||
3203 | * t2: T2, event->rb = new rb | ||
3204 | * t3: T2, ring_buffer_detach(old_rb) | ||
3205 | * t4: T1, ring_buffer_attach(rb1) | ||
3206 | * t5: T1, poll_wait(event->waitq) | ||
3207 | * | ||
3208 | * To avoid this problem, we grab mmap_mutex in perf_poll() | ||
3209 | * thereby ensuring that the assignment of the new ring buffer | ||
3210 | * and the detachment of the old buffer appear atomic to perf_poll() | ||
3211 | */ | ||
3212 | mutex_lock(&event->mmap_mutex); | ||
3213 | |||
3193 | rcu_read_lock(); | 3214 | rcu_read_lock(); |
3194 | rb = rcu_dereference(event->rb); | 3215 | rb = rcu_dereference(event->rb); |
3195 | if (rb) | 3216 | if (rb) { |
3217 | ring_buffer_attach(event, rb); | ||
3196 | events = atomic_xchg(&rb->poll, 0); | 3218 | events = atomic_xchg(&rb->poll, 0); |
3219 | } | ||
3197 | rcu_read_unlock(); | 3220 | rcu_read_unlock(); |
3198 | 3221 | ||
3222 | mutex_unlock(&event->mmap_mutex); | ||
3223 | |||
3199 | poll_wait(file, &event->waitq, wait); | 3224 | poll_wait(file, &event->waitq, wait); |
3200 | 3225 | ||
3201 | return events; | 3226 | return events; |
@@ -3496,6 +3521,49 @@ unlock: | |||
3496 | return ret; | 3521 | return ret; |
3497 | } | 3522 | } |
3498 | 3523 | ||
3524 | static void ring_buffer_attach(struct perf_event *event, | ||
3525 | struct ring_buffer *rb) | ||
3526 | { | ||
3527 | unsigned long flags; | ||
3528 | |||
3529 | if (!list_empty(&event->rb_entry)) | ||
3530 | return; | ||
3531 | |||
3532 | spin_lock_irqsave(&rb->event_lock, flags); | ||
3533 | if (!list_empty(&event->rb_entry)) | ||
3534 | goto unlock; | ||
3535 | |||
3536 | list_add(&event->rb_entry, &rb->event_list); | ||
3537 | unlock: | ||
3538 | spin_unlock_irqrestore(&rb->event_lock, flags); | ||
3539 | } | ||
3540 | |||
3541 | static void ring_buffer_detach(struct perf_event *event, | ||
3542 | struct ring_buffer *rb) | ||
3543 | { | ||
3544 | unsigned long flags; | ||
3545 | |||
3546 | if (list_empty(&event->rb_entry)) | ||
3547 | return; | ||
3548 | |||
3549 | spin_lock_irqsave(&rb->event_lock, flags); | ||
3550 | list_del_init(&event->rb_entry); | ||
3551 | wake_up_all(&event->waitq); | ||
3552 | spin_unlock_irqrestore(&rb->event_lock, flags); | ||
3553 | } | ||
3554 | |||
3555 | static void ring_buffer_wakeup(struct perf_event *event) | ||
3556 | { | ||
3557 | struct ring_buffer *rb; | ||
3558 | |||
3559 | rcu_read_lock(); | ||
3560 | rb = rcu_dereference(event->rb); | ||
3561 | list_for_each_entry_rcu(event, &rb->event_list, rb_entry) { | ||
3562 | wake_up_all(&event->waitq); | ||
3563 | } | ||
3564 | rcu_read_unlock(); | ||
3565 | } | ||
3566 | |||
3499 | static void rb_free_rcu(struct rcu_head *rcu_head) | 3567 | static void rb_free_rcu(struct rcu_head *rcu_head) |
3500 | { | 3568 | { |
3501 | struct ring_buffer *rb; | 3569 | struct ring_buffer *rb; |
@@ -3521,9 +3589,19 @@ static struct ring_buffer *ring_buffer_get(struct perf_event *event) | |||
3521 | 3589 | ||
3522 | static void ring_buffer_put(struct ring_buffer *rb) | 3590 | static void ring_buffer_put(struct ring_buffer *rb) |
3523 | { | 3591 | { |
3592 | struct perf_event *event, *n; | ||
3593 | unsigned long flags; | ||
3594 | |||
3524 | if (!atomic_dec_and_test(&rb->refcount)) | 3595 | if (!atomic_dec_and_test(&rb->refcount)) |
3525 | return; | 3596 | return; |
3526 | 3597 | ||
3598 | spin_lock_irqsave(&rb->event_lock, flags); | ||
3599 | list_for_each_entry_safe(event, n, &rb->event_list, rb_entry) { | ||
3600 | list_del_init(&event->rb_entry); | ||
3601 | wake_up_all(&event->waitq); | ||
3602 | } | ||
3603 | spin_unlock_irqrestore(&rb->event_lock, flags); | ||
3604 | |||
3527 | call_rcu(&rb->rcu_head, rb_free_rcu); | 3605 | call_rcu(&rb->rcu_head, rb_free_rcu); |
3528 | } | 3606 | } |
3529 | 3607 | ||
@@ -3546,6 +3624,7 @@ static void perf_mmap_close(struct vm_area_struct *vma) | |||
3546 | atomic_long_sub((size >> PAGE_SHIFT) + 1, &user->locked_vm); | 3624 | atomic_long_sub((size >> PAGE_SHIFT) + 1, &user->locked_vm); |
3547 | vma->vm_mm->pinned_vm -= event->mmap_locked; | 3625 | vma->vm_mm->pinned_vm -= event->mmap_locked; |
3548 | rcu_assign_pointer(event->rb, NULL); | 3626 | rcu_assign_pointer(event->rb, NULL); |
3627 | ring_buffer_detach(event, rb); | ||
3549 | mutex_unlock(&event->mmap_mutex); | 3628 | mutex_unlock(&event->mmap_mutex); |
3550 | 3629 | ||
3551 | ring_buffer_put(rb); | 3630 | ring_buffer_put(rb); |
@@ -3700,7 +3779,7 @@ static const struct file_operations perf_fops = { | |||
3700 | 3779 | ||
3701 | void perf_event_wakeup(struct perf_event *event) | 3780 | void perf_event_wakeup(struct perf_event *event) |
3702 | { | 3781 | { |
3703 | wake_up_all(&event->waitq); | 3782 | ring_buffer_wakeup(event); |
3704 | 3783 | ||
3705 | if (event->pending_kill) { | 3784 | if (event->pending_kill) { |
3706 | kill_fasync(&event->fasync, SIGIO, event->pending_kill); | 3785 | kill_fasync(&event->fasync, SIGIO, event->pending_kill); |
@@ -5822,6 +5901,8 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, | |||
5822 | INIT_LIST_HEAD(&event->group_entry); | 5901 | INIT_LIST_HEAD(&event->group_entry); |
5823 | INIT_LIST_HEAD(&event->event_entry); | 5902 | INIT_LIST_HEAD(&event->event_entry); |
5824 | INIT_LIST_HEAD(&event->sibling_list); | 5903 | INIT_LIST_HEAD(&event->sibling_list); |
5904 | INIT_LIST_HEAD(&event->rb_entry); | ||
5905 | |||
5825 | init_waitqueue_head(&event->waitq); | 5906 | init_waitqueue_head(&event->waitq); |
5826 | init_irq_work(&event->pending, perf_pending_event); | 5907 | init_irq_work(&event->pending, perf_pending_event); |
5827 | 5908 | ||
@@ -6028,6 +6109,8 @@ set: | |||
6028 | 6109 | ||
6029 | old_rb = event->rb; | 6110 | old_rb = event->rb; |
6030 | rcu_assign_pointer(event->rb, rb); | 6111 | rcu_assign_pointer(event->rb, rb); |
6112 | if (old_rb) | ||
6113 | ring_buffer_detach(event, old_rb); | ||
6031 | ret = 0; | 6114 | ret = 0; |
6032 | unlock: | 6115 | unlock: |
6033 | mutex_unlock(&event->mmap_mutex); | 6116 | mutex_unlock(&event->mmap_mutex); |
diff --git a/kernel/events/internal.h b/kernel/events/internal.h index 09097dd8116c..64568a699375 100644 --- a/kernel/events/internal.h +++ b/kernel/events/internal.h | |||
@@ -22,6 +22,9 @@ struct ring_buffer { | |||
22 | local_t lost; /* nr records lost */ | 22 | local_t lost; /* nr records lost */ |
23 | 23 | ||
24 | long watermark; /* wakeup watermark */ | 24 | long watermark; /* wakeup watermark */ |
25 | /* poll crap */ | ||
26 | spinlock_t event_lock; | ||
27 | struct list_head event_list; | ||
25 | 28 | ||
26 | struct perf_event_mmap_page *user_page; | 29 | struct perf_event_mmap_page *user_page; |
27 | void *data_pages[0]; | 30 | void *data_pages[0]; |
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index a2a29205cc0f..7f3011c6b57f 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c | |||
@@ -209,6 +209,9 @@ ring_buffer_init(struct ring_buffer *rb, long watermark, int flags) | |||
209 | rb->writable = 1; | 209 | rb->writable = 1; |
210 | 210 | ||
211 | atomic_set(&rb->refcount, 1); | 211 | atomic_set(&rb->refcount, 1); |
212 | |||
213 | INIT_LIST_HEAD(&rb->event_list); | ||
214 | spin_lock_init(&rb->event_lock); | ||
212 | } | 215 | } |
213 | 216 | ||
214 | #ifndef CONFIG_PERF_USE_VMALLOC | 217 | #ifndef CONFIG_PERF_USE_VMALLOC |
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 816d3d074979..d6e7926dcd26 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c | |||
@@ -1686,7 +1686,7 @@ static int replace_system_preds(struct event_subsystem *system, | |||
1686 | * replace the filter for the call. | 1686 | * replace the filter for the call. |
1687 | */ | 1687 | */ |
1688 | filter = call->filter; | 1688 | filter = call->filter; |
1689 | call->filter = filter_item->filter; | 1689 | rcu_assign_pointer(call->filter, filter_item->filter); |
1690 | filter_item->filter = filter; | 1690 | filter_item->filter = filter; |
1691 | 1691 | ||
1692 | fail = false; | 1692 | fail = false; |
@@ -1741,7 +1741,7 @@ int apply_event_filter(struct ftrace_event_call *call, char *filter_string) | |||
1741 | filter = call->filter; | 1741 | filter = call->filter; |
1742 | if (!filter) | 1742 | if (!filter) |
1743 | goto out_unlock; | 1743 | goto out_unlock; |
1744 | call->filter = NULL; | 1744 | RCU_INIT_POINTER(call->filter, NULL); |
1745 | /* Make sure the filter is not being used */ | 1745 | /* Make sure the filter is not being used */ |
1746 | synchronize_sched(); | 1746 | synchronize_sched(); |
1747 | __free_filter(filter); | 1747 | __free_filter(filter); |
@@ -1782,7 +1782,7 @@ out: | |||
1782 | * string | 1782 | * string |
1783 | */ | 1783 | */ |
1784 | tmp = call->filter; | 1784 | tmp = call->filter; |
1785 | call->filter = filter; | 1785 | rcu_assign_pointer(call->filter, filter); |
1786 | if (tmp) { | 1786 | if (tmp) { |
1787 | /* Make sure the call is done with the filter */ | 1787 | /* Make sure the call is done with the filter */ |
1788 | synchronize_sched(); | 1788 | synchronize_sched(); |