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...
Showing the changes to kernel (2 of 3 files). Show all
Unified · Split
| @@ -113,12 +113,12 @@ static unsigned int max_task_bp_pinned(int cpu, enum bp_type_idx type) · 112 unmodified lines | |||
| 113 | */ | 113 | */ |
| 114 | static int task_bp_pinned(struct perf_event *bp, enum bp_type_idx type) | 114 | static int task_bp_pinned(struct perf_event *bp, enum bp_type_idx type) |
| 115 | { | 115 | { |
| 116 | struct perf_event_context *ctx = bp->ctx; | 116 | struct task_struct *tsk = bp->hw.bp_target; |
| 117 | struct perf_event *iter; | 117 | struct perf_event *iter; |
| 118 | int count = 0; | 118 | int count = 0; |
| 119 | 119 | ||
| 120 | list_for_each_entry(iter, &bp_task_head, hw.bp_list) { | 120 | 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) |
| 122 | count += hw_breakpoint_weight(iter); | 122 | count += hw_breakpoint_weight(iter); |
| 123 | } | 123 | } |
| 124 | 124 | ||
| @@ -134,7 +134,7 @@ fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp, · 9 unmodified lines | |||
| 134 | enum bp_type_idx type) | 134 | enum bp_type_idx type) |
| 135 | { | 135 | { |
| 136 | int cpu = bp->cpu; | 136 | int cpu = bp->cpu; |
| 137 | struct task_struct *tsk = bp->ctx->task; | 137 | struct task_struct *tsk = bp->hw.bp_target; |
| 138 | 138 | ||
| 139 | if (cpu >= 0) { | 139 | if (cpu >= 0) { |
| 140 | slots->pinned = per_cpu(nr_cpu_bp_pinned[type], cpu); | 140 | 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 | |||
| 213 | int weight) | 213 | int weight) |
| 214 | { | 214 | { |
| 215 | int cpu = bp->cpu; | 215 | int cpu = bp->cpu; |
| 216 | struct task_struct *tsk = bp->ctx->task; | 216 | struct task_struct *tsk = bp->hw.bp_target; |
| 217 | 217 | ||
| 218 | /* Pinned counter cpu profiling */ | 218 | /* Pinned counter cpu profiling */ |
| 219 | if (!tsk) { | 219 | if (!tsk) { |
| @@ -5255,9 +5255,10 @@ unlock: · 5254 unmodified lines | |||
| 5255 | */ | 5255 | */ |
| 5256 | static struct perf_event * | 5256 | static struct perf_event * |
| 5257 | perf_event_alloc(struct perf_event_attr *attr, int cpu, | 5257 | perf_event_alloc(struct perf_event_attr *attr, int cpu, |
| 5258 | struct perf_event *group_leader, | 5258 | struct task_struct *task, |
| 5259 | struct perf_event *parent_event, | 5259 | struct perf_event *group_leader, |
| 5260 | perf_overflow_handler_t overflow_handler) | 5260 | struct perf_event *parent_event, |
| 5261 | perf_overflow_handler_t overflow_handler) | ||
| 5261 | { | 5262 | { |
| 5262 | struct pmu *pmu; | 5263 | struct pmu *pmu; |
| 5263 | struct perf_event *event; | 5264 | struct perf_event *event; |
| @@ -5299,6 +5300,17 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, · 35 unmodified lines | |||
| 5299 | 5300 | ||
| 5300 | event->state = PERF_EVENT_STATE_INACTIVE; | 5301 | event->state = PERF_EVENT_STATE_INACTIVE; |
| 5301 | 5302 | ||
| 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 | |||
| 5302 | if (!overflow_handler && parent_event) | 5314 | if (!overflow_handler && parent_event) |
| 5303 | overflow_handler = parent_event->overflow_handler; | 5315 | overflow_handler = parent_event->overflow_handler; |
| 5304 | 5316 | ||
| @@ -5559,7 +5571,7 @@ SYSCALL_DEFINE5(perf_event_open, · 254 unmodified lines | |||
| 5559 | } | 5571 | } |
| 5560 | } | 5572 | } |
| 5561 | 5573 | ||
| 5562 | event = perf_event_alloc(&attr, cpu, group_leader, NULL, NULL); | 5574 | event = perf_event_alloc(&attr, cpu, task, group_leader, NULL, NULL); |
| 5563 | if (IS_ERR(event)) { | 5575 | if (IS_ERR(event)) { |
| 5564 | err = PTR_ERR(event); | 5576 | err = PTR_ERR(event); |
| 5565 | goto err_task; | 5577 | goto err_task; |
| @@ -5728,7 +5740,7 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, · 162 unmodified lines | |||
| 5728 | * Get the target context (task or percpu): | 5740 | * Get the target context (task or percpu): |
| 5729 | */ | 5741 | */ |
| 5730 | 5742 | ||
| 5731 | event = perf_event_alloc(attr, cpu, NULL, NULL, overflow_handler); | 5743 | event = perf_event_alloc(attr, cpu, task, NULL, NULL, overflow_handler); |
| 5732 | if (IS_ERR(event)) { | 5744 | if (IS_ERR(event)) { |
| 5733 | err = PTR_ERR(event); | 5745 | err = PTR_ERR(event); |
| 5734 | goto err; | 5746 | goto err; |
| @@ -5996,6 +6008,7 @@ inherit_event(struct perf_event *parent_event, · 261 unmodified lines | |||
| 5996 | 6008 | ||
| 5997 | child_event = perf_event_alloc(&parent_event->attr, | 6009 | child_event = perf_event_alloc(&parent_event->attr, |
| 5998 | parent_event->cpu, | 6010 | parent_event->cpu, |
| 6011 | child, | ||
| 5999 | group_leader, parent_event, | 6012 | group_leader, parent_event, |
| 6000 | NULL); | 6013 | NULL); |
| 6001 | if (IS_ERR(child_event)) | 6014 | if (IS_ERR(child_event)) |