History

perf, hw_breakpoint: Fix crash in hw_breakpoint creation

hw_breakpoint creation needs to account stuff per-task to ensure there is always sufficient hardware resources to back these things due to ptrace. With the perf per pmu context changes the event initialization no longer has access to the event context, for the simple reason that we need to first find the pmu (result of initialization) before we can find the context. This makes hw_breakpoints unhappy, because it can no longer do per task accounting, cure this by frobbing a task pointer in the event::hw bits for now...

Peter Zijlstra committed 16 years ago

Commit d580ff81 parent c6be5a53 files · +29 −9Browse files

Unified · Split

3 files
  1. include/linux/perf_event.h +7 −0
  2. kernel/hw_breakpoint.c +4 −4
  3. kernel/perf_event.c +18 −5
include/linux/perf_event.h+7−0
@@ -536,6 +536,12 @@ struct hw_perf_event { · 535 unmodified lines
536536 struct { /* breakpoint */
537537 struct arch_hw_breakpoint info;
538538 struct list_head bp_list;
539+ /*
540+ * Crufty hack to avoid the chicken and egg
541+ * problem hw_breakpoint has with context
542+ * creation and event initalization.
543+ */
544+ struct task_struct *bp_target;
539545 };
540546 #endif
541547 };
@@ -693,6 +699,7 @@ struct swevent_hlist { · 151 unmodified lines
693699
694700 #define PERF_ATTACH_CONTEXT 0x01
695701 #define PERF_ATTACH_GROUP 0x02
702+#define PERF_ATTACH_TASK 0x04
696703
697704 /**
698705 * struct perf_event - performance event kernel representation:
kernel/hw_breakpoint.c+4−4
@@ -113,12 +113,12 @@ static unsigned int max_task_bp_pinned(int cpu, enum bp_type_idx type) · 112 unmodified lines
113113 */
114114 static int task_bp_pinned(struct perf_event *bp, enum bp_type_idx type)
115115 {
116− struct perf_event_context *ctx = bp->ctx;
116+ struct task_struct *tsk = bp->hw.bp_target;
117117 struct perf_event *iter;
118118 int count = 0;
119119
120120 list_for_each_entry(iter, &bp_task_head, hw.bp_list) {
121− if (iter->ctx == ctx && find_slot_idx(iter) == type)
121+ if (iter->hw.bp_target == tsk && find_slot_idx(iter) == type)
122122 count += hw_breakpoint_weight(iter);
123123 }
124124
@@ -134,7 +134,7 @@ fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp, · 9 unmodified lines
134134 enum bp_type_idx type)
135135 {
136136 int cpu = bp->cpu;
137− struct task_struct *tsk = bp->ctx->task;
137+ struct task_struct *tsk = bp->hw.bp_target;
138138
139139 if (cpu >= 0) {
140140 slots->pinned = per_cpu(nr_cpu_bp_pinned[type], cpu);
@@ -213,7 +213,7 @@ toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type, · 72 unmodified lines
213213 int weight)
214214 {
215215 int cpu = bp->cpu;
216− struct task_struct *tsk = bp->ctx->task;
216+ struct task_struct *tsk = bp->hw.bp_target;
217217
218218 /* Pinned counter cpu profiling */
219219 if (!tsk) {
kernel/perf_event.c+18−5
@@ -5255,9 +5255,10 @@ unlock: · 5254 unmodified lines
52555255 */
52565256 static struct perf_event *
52575257 perf_event_alloc(struct perf_event_attr *attr, int cpu,
5258− struct perf_event *group_leader,
5259− struct perf_event *parent_event,
5260− perf_overflow_handler_t overflow_handler)
5258+ struct task_struct *task,
5259+ struct perf_event *group_leader,
5260+ struct perf_event *parent_event,
5261+ perf_overflow_handler_t overflow_handler)
52615262 {
52625263 struct pmu *pmu;
52635264 struct perf_event *event;
@@ -5299,6 +5300,17 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, · 35 unmodified lines
52995300
53005301 event->state = PERF_EVENT_STATE_INACTIVE;
53015302
5303+ if (task) {
5304+ event->attach_state = PERF_ATTACH_TASK;
5305+#ifdef CONFIG_HAVE_HW_BREAKPOINT
5306+ /*
5307+ * hw_breakpoint is a bit difficult here..
5308+ */
5309+ if (attr->type == PERF_TYPE_BREAKPOINT)
5310+ event->hw.bp_target = task;
5311+#endif
5312+ }
5313+
53025314 if (!overflow_handler && parent_event)
53035315 overflow_handler = parent_event->overflow_handler;
53045316
@@ -5559,7 +5571,7 @@ SYSCALL_DEFINE5(perf_event_open, · 254 unmodified lines
55595571 }
55605572 }
55615573
5562− event = perf_event_alloc(&attr, cpu, group_leader, NULL, NULL);
5574+ event = perf_event_alloc(&attr, cpu, task, group_leader, NULL, NULL);
55635575 if (IS_ERR(event)) {
55645576 err = PTR_ERR(event);
55655577 goto err_task;
@@ -5728,7 +5740,7 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, · 162 unmodified lines
57285740 * Get the target context (task or percpu):
57295741 */
57305742
5731− event = perf_event_alloc(attr, cpu, NULL, NULL, overflow_handler);
5743+ event = perf_event_alloc(attr, cpu, task, NULL, NULL, overflow_handler);
57325744 if (IS_ERR(event)) {
57335745 err = PTR_ERR(event);
57345746 goto err;
@@ -5996,6 +6008,7 @@ inherit_event(struct perf_event *parent_event, · 261 unmodified lines
59966008
59976009 child_event = perf_event_alloc(&parent_event->attr,
59986010 parent_event->cpu,
6011+ child,
59996012 group_leader, parent_event,
60006013 NULL);
60016014 if (IS_ERR(child_event))