diff options
Diffstat (limited to 'kernel/events/core.c')
| -rw-r--r-- | kernel/events/core.c | 49 | 
1 files changed, 45 insertions, 4 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c index d7d71d6ec97..f1cf0edeb39 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1645,6 +1645,8 @@ perf_install_in_context(struct perf_event_context *ctx,  	lockdep_assert_held(&ctx->mutex);  	event->ctx = ctx; +	if (event->cpu != -1) +		event->cpu = cpu;  	if (!task) {  		/* @@ -6252,6 +6254,8 @@ SYSCALL_DEFINE5(perf_event_open,  		}  	} +	get_online_cpus(); +  	event = perf_event_alloc(&attr, cpu, task, group_leader, NULL,  				 NULL, NULL);  	if (IS_ERR(event)) { @@ -6304,7 +6308,7 @@ SYSCALL_DEFINE5(perf_event_open,  	/*  	 * Get the target context (task or percpu):  	 */ -	ctx = find_get_context(pmu, task, cpu); +	ctx = find_get_context(pmu, task, event->cpu);  	if (IS_ERR(ctx)) {  		err = PTR_ERR(ctx);  		goto err_alloc; @@ -6377,20 +6381,23 @@ SYSCALL_DEFINE5(perf_event_open,  	mutex_lock(&ctx->mutex);  	if (move_group) { -		perf_install_in_context(ctx, group_leader, cpu); +		synchronize_rcu(); +		perf_install_in_context(ctx, group_leader, event->cpu);  		get_ctx(ctx);  		list_for_each_entry(sibling, &group_leader->sibling_list,  				    group_entry) { -			perf_install_in_context(ctx, sibling, cpu); +			perf_install_in_context(ctx, sibling, event->cpu);  			get_ctx(ctx);  		}  	} -	perf_install_in_context(ctx, event, cpu); +	perf_install_in_context(ctx, event, event->cpu);  	++ctx->generation;  	perf_unpin_context(ctx);  	mutex_unlock(&ctx->mutex); +	put_online_cpus(); +  	event->owner = current;  	mutex_lock(¤t->perf_event_mutex); @@ -6419,6 +6426,7 @@ err_context:  err_alloc:  	free_event(event);  err_task: +	put_online_cpus();  	if (task)  		put_task_struct(task);  err_group_fd: @@ -6479,6 +6487,39 @@ err:  }  EXPORT_SYMBOL_GPL(perf_event_create_kernel_counter); +void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu) +{ +	struct perf_event_context *src_ctx; +	struct perf_event_context *dst_ctx; +	struct perf_event *event, *tmp; +	LIST_HEAD(events); + +	src_ctx = &per_cpu_ptr(pmu->pmu_cpu_context, src_cpu)->ctx; +	dst_ctx = &per_cpu_ptr(pmu->pmu_cpu_context, dst_cpu)->ctx; + +	mutex_lock(&src_ctx->mutex); +	list_for_each_entry_safe(event, tmp, &src_ctx->event_list, +				 event_entry) { +		perf_remove_from_context(event); +		put_ctx(src_ctx); +		list_add(&event->event_entry, &events); +	} +	mutex_unlock(&src_ctx->mutex); + +	synchronize_rcu(); + +	mutex_lock(&dst_ctx->mutex); +	list_for_each_entry_safe(event, tmp, &events, event_entry) { +		list_del(&event->event_entry); +		if (event->state >= PERF_EVENT_STATE_OFF) +			event->state = PERF_EVENT_STATE_INACTIVE; +		perf_install_in_context(dst_ctx, event, dst_cpu); +		get_ctx(dst_ctx); +	} +	mutex_unlock(&dst_ctx->mutex); +} +EXPORT_SYMBOL_GPL(perf_pmu_migrate_context); +  static void sync_child_event(struct perf_event *child_event,  			       struct task_struct *child)  {  |