diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2010-09-06 08:48:15 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-09-09 14:46:32 -0400 |
commit | b28ab83c595e767f2028276b7398d17f2253cec0 (patch) | |
tree | ae52b2d4c10d1317ec7a5906bea56847e95b5f43 /kernel/perf_event.c | |
parent | c3f00c70276d8ae82578c8b773e2db657f69a478 (diff) |
perf: Remove the swevent hash-table from the cpu context
Separate the swevent hash-table from the cpu_context bits in
preparation for per pmu cpu contexts.
This keeps the swevent hash a global entity.
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus <paulus@samba.org>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Yanmin <yanmin_zhang@linux.intel.com>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/perf_event.c')
-rw-r--r-- | kernel/perf_event.c | 104 |
1 files changed, 58 insertions, 46 deletions
diff --git a/kernel/perf_event.c b/kernel/perf_event.c index a3c86a8335c4..2c47ed6c4f26 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c | |||
@@ -4154,6 +4154,17 @@ int perf_event_overflow(struct perf_event *event, int nmi, | |||
4154 | * Generic software event infrastructure | 4154 | * Generic software event infrastructure |
4155 | */ | 4155 | */ |
4156 | 4156 | ||
4157 | struct swevent_htable { | ||
4158 | struct swevent_hlist *swevent_hlist; | ||
4159 | struct mutex hlist_mutex; | ||
4160 | int hlist_refcount; | ||
4161 | |||
4162 | /* Recursion avoidance in each contexts */ | ||
4163 | int recursion[PERF_NR_CONTEXTS]; | ||
4164 | }; | ||
4165 | |||
4166 | static DEFINE_PER_CPU(struct swevent_htable, swevent_htable); | ||
4167 | |||
4157 | /* | 4168 | /* |
4158 | * We directly increment event->count and keep a second value in | 4169 | * We directly increment event->count and keep a second value in |
4159 | * event->hw.period_left to count intervals. This period event | 4170 | * event->hw.period_left to count intervals. This period event |
@@ -4286,11 +4297,11 @@ __find_swevent_head(struct swevent_hlist *hlist, u64 type, u32 event_id) | |||
4286 | 4297 | ||
4287 | /* For the read side: events when they trigger */ | 4298 | /* For the read side: events when they trigger */ |
4288 | static inline struct hlist_head * | 4299 | static inline struct hlist_head * |
4289 | find_swevent_head_rcu(struct perf_cpu_context *ctx, u64 type, u32 event_id) | 4300 | find_swevent_head_rcu(struct swevent_htable *swhash, u64 type, u32 event_id) |
4290 | { | 4301 | { |
4291 | struct swevent_hlist *hlist; | 4302 | struct swevent_hlist *hlist; |
4292 | 4303 | ||
4293 | hlist = rcu_dereference(ctx->swevent_hlist); | 4304 | hlist = rcu_dereference(swhash->swevent_hlist); |
4294 | if (!hlist) | 4305 | if (!hlist) |
4295 | return NULL; | 4306 | return NULL; |
4296 | 4307 | ||
@@ -4299,7 +4310,7 @@ find_swevent_head_rcu(struct perf_cpu_context *ctx, u64 type, u32 event_id) | |||
4299 | 4310 | ||
4300 | /* For the event head insertion and removal in the hlist */ | 4311 | /* For the event head insertion and removal in the hlist */ |
4301 | static inline struct hlist_head * | 4312 | static inline struct hlist_head * |
4302 | find_swevent_head(struct perf_cpu_context *ctx, struct perf_event *event) | 4313 | find_swevent_head(struct swevent_htable *swhash, struct perf_event *event) |
4303 | { | 4314 | { |
4304 | struct swevent_hlist *hlist; | 4315 | struct swevent_hlist *hlist; |
4305 | u32 event_id = event->attr.config; | 4316 | u32 event_id = event->attr.config; |
@@ -4310,7 +4321,7 @@ find_swevent_head(struct perf_cpu_context *ctx, struct perf_event *event) | |||
4310 | * and release. Which makes the protected version suitable here. | 4321 | * and release. Which makes the protected version suitable here. |
4311 | * The context lock guarantees that. | 4322 | * The context lock guarantees that. |
4312 | */ | 4323 | */ |
4313 | hlist = rcu_dereference_protected(ctx->swevent_hlist, | 4324 | hlist = rcu_dereference_protected(swhash->swevent_hlist, |
4314 | lockdep_is_held(&event->ctx->lock)); | 4325 | lockdep_is_held(&event->ctx->lock)); |
4315 | if (!hlist) | 4326 | if (!hlist) |
4316 | return NULL; | 4327 | return NULL; |
@@ -4323,17 +4334,13 @@ static void do_perf_sw_event(enum perf_type_id type, u32 event_id, | |||
4323 | struct perf_sample_data *data, | 4334 | struct perf_sample_data *data, |
4324 | struct pt_regs *regs) | 4335 | struct pt_regs *regs) |
4325 | { | 4336 | { |
4326 | struct perf_cpu_context *cpuctx; | 4337 | struct swevent_htable *swhash = &__get_cpu_var(swevent_htable); |
4327 | struct perf_event *event; | 4338 | struct perf_event *event; |
4328 | struct hlist_node *node; | 4339 | struct hlist_node *node; |
4329 | struct hlist_head *head; | 4340 | struct hlist_head *head; |
4330 | 4341 | ||
4331 | cpuctx = &__get_cpu_var(perf_cpu_context); | ||
4332 | |||
4333 | rcu_read_lock(); | 4342 | rcu_read_lock(); |
4334 | 4343 | head = find_swevent_head_rcu(swhash, type, event_id); | |
4335 | head = find_swevent_head_rcu(cpuctx, type, event_id); | ||
4336 | |||
4337 | if (!head) | 4344 | if (!head) |
4338 | goto end; | 4345 | goto end; |
4339 | 4346 | ||
@@ -4347,17 +4354,17 @@ end: | |||
4347 | 4354 | ||
4348 | int perf_swevent_get_recursion_context(void) | 4355 | int perf_swevent_get_recursion_context(void) |
4349 | { | 4356 | { |
4350 | struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); | 4357 | struct swevent_htable *swhash = &__get_cpu_var(swevent_htable); |
4351 | 4358 | ||
4352 | return get_recursion_context(cpuctx->recursion); | 4359 | return get_recursion_context(swhash->recursion); |
4353 | } | 4360 | } |
4354 | EXPORT_SYMBOL_GPL(perf_swevent_get_recursion_context); | 4361 | EXPORT_SYMBOL_GPL(perf_swevent_get_recursion_context); |
4355 | 4362 | ||
4356 | void inline perf_swevent_put_recursion_context(int rctx) | 4363 | void inline perf_swevent_put_recursion_context(int rctx) |
4357 | { | 4364 | { |
4358 | struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); | 4365 | struct swevent_htable *swhash = &__get_cpu_var(swevent_htable); |
4359 | 4366 | ||
4360 | put_recursion_context(cpuctx->recursion, rctx); | 4367 | put_recursion_context(swhash->recursion, rctx); |
4361 | } | 4368 | } |
4362 | 4369 | ||
4363 | void __perf_sw_event(u32 event_id, u64 nr, int nmi, | 4370 | void __perf_sw_event(u32 event_id, u64 nr, int nmi, |
@@ -4385,12 +4392,10 @@ static void perf_swevent_read(struct perf_event *event) | |||
4385 | 4392 | ||
4386 | static int perf_swevent_add(struct perf_event *event, int flags) | 4393 | static int perf_swevent_add(struct perf_event *event, int flags) |
4387 | { | 4394 | { |
4395 | struct swevent_htable *swhash = &__get_cpu_var(swevent_htable); | ||
4388 | struct hw_perf_event *hwc = &event->hw; | 4396 | struct hw_perf_event *hwc = &event->hw; |
4389 | struct perf_cpu_context *cpuctx; | ||
4390 | struct hlist_head *head; | 4397 | struct hlist_head *head; |
4391 | 4398 | ||
4392 | cpuctx = &__get_cpu_var(perf_cpu_context); | ||
4393 | |||
4394 | if (hwc->sample_period) { | 4399 | if (hwc->sample_period) { |
4395 | hwc->last_period = hwc->sample_period; | 4400 | hwc->last_period = hwc->sample_period; |
4396 | perf_swevent_set_period(event); | 4401 | perf_swevent_set_period(event); |
@@ -4398,7 +4403,7 @@ static int perf_swevent_add(struct perf_event *event, int flags) | |||
4398 | 4403 | ||
4399 | hwc->state = !(flags & PERF_EF_START); | 4404 | hwc->state = !(flags & PERF_EF_START); |
4400 | 4405 | ||
4401 | head = find_swevent_head(cpuctx, event); | 4406 | head = find_swevent_head(swhash, event); |
4402 | if (WARN_ON_ONCE(!head)) | 4407 | if (WARN_ON_ONCE(!head)) |
4403 | return -EINVAL; | 4408 | return -EINVAL; |
4404 | 4409 | ||
@@ -4424,10 +4429,10 @@ static void perf_swevent_stop(struct perf_event *event, int flags) | |||
4424 | 4429 | ||
4425 | /* Deref the hlist from the update side */ | 4430 | /* Deref the hlist from the update side */ |
4426 | static inline struct swevent_hlist * | 4431 | static inline struct swevent_hlist * |
4427 | swevent_hlist_deref(struct perf_cpu_context *cpuctx) | 4432 | swevent_hlist_deref(struct swevent_htable *swhash) |
4428 | { | 4433 | { |
4429 | return rcu_dereference_protected(cpuctx->swevent_hlist, | 4434 | return rcu_dereference_protected(swhash->swevent_hlist, |
4430 | lockdep_is_held(&cpuctx->hlist_mutex)); | 4435 | lockdep_is_held(&swhash->hlist_mutex)); |
4431 | } | 4436 | } |
4432 | 4437 | ||
4433 | static void swevent_hlist_release_rcu(struct rcu_head *rcu_head) | 4438 | static void swevent_hlist_release_rcu(struct rcu_head *rcu_head) |
@@ -4438,27 +4443,27 @@ static void swevent_hlist_release_rcu(struct rcu_head *rcu_head) | |||
4438 | kfree(hlist); | 4443 | kfree(hlist); |
4439 | } | 4444 | } |
4440 | 4445 | ||
4441 | static void swevent_hlist_release(struct perf_cpu_context *cpuctx) | 4446 | static void swevent_hlist_release(struct swevent_htable *swhash) |
4442 | { | 4447 | { |
4443 | struct swevent_hlist *hlist = swevent_hlist_deref(cpuctx); | 4448 | struct swevent_hlist *hlist = swevent_hlist_deref(swhash); |
4444 | 4449 | ||
4445 | if (!hlist) | 4450 | if (!hlist) |
4446 | return; | 4451 | return; |
4447 | 4452 | ||
4448 | rcu_assign_pointer(cpuctx->swevent_hlist, NULL); | 4453 | rcu_assign_pointer(swhash->swevent_hlist, NULL); |
4449 | call_rcu(&hlist->rcu_head, swevent_hlist_release_rcu); | 4454 | call_rcu(&hlist->rcu_head, swevent_hlist_release_rcu); |
4450 | } | 4455 | } |
4451 | 4456 | ||
4452 | static void swevent_hlist_put_cpu(struct perf_event *event, int cpu) | 4457 | static void swevent_hlist_put_cpu(struct perf_event *event, int cpu) |
4453 | { | 4458 | { |
4454 | struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); | 4459 | struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu); |
4455 | 4460 | ||
4456 | mutex_lock(&cpuctx->hlist_mutex); | 4461 | mutex_lock(&swhash->hlist_mutex); |
4457 | 4462 | ||
4458 | if (!--cpuctx->hlist_refcount) | 4463 | if (!--swhash->hlist_refcount) |
4459 | swevent_hlist_release(cpuctx); | 4464 | swevent_hlist_release(swhash); |
4460 | 4465 | ||
4461 | mutex_unlock(&cpuctx->hlist_mutex); | 4466 | mutex_unlock(&swhash->hlist_mutex); |
4462 | } | 4467 | } |
4463 | 4468 | ||
4464 | static void swevent_hlist_put(struct perf_event *event) | 4469 | static void swevent_hlist_put(struct perf_event *event) |
@@ -4476,12 +4481,12 @@ static void swevent_hlist_put(struct perf_event *event) | |||
4476 | 4481 | ||
4477 | static int swevent_hlist_get_cpu(struct perf_event *event, int cpu) | 4482 | static int swevent_hlist_get_cpu(struct perf_event *event, int cpu) |
4478 | { | 4483 | { |
4479 | struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); | 4484 | struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu); |
4480 | int err = 0; | 4485 | int err = 0; |
4481 | 4486 | ||
4482 | mutex_lock(&cpuctx->hlist_mutex); | 4487 | mutex_lock(&swhash->hlist_mutex); |
4483 | 4488 | ||
4484 | if (!swevent_hlist_deref(cpuctx) && cpu_online(cpu)) { | 4489 | if (!swevent_hlist_deref(swhash) && cpu_online(cpu)) { |
4485 | struct swevent_hlist *hlist; | 4490 | struct swevent_hlist *hlist; |
4486 | 4491 | ||
4487 | hlist = kzalloc(sizeof(*hlist), GFP_KERNEL); | 4492 | hlist = kzalloc(sizeof(*hlist), GFP_KERNEL); |
@@ -4489,11 +4494,11 @@ static int swevent_hlist_get_cpu(struct perf_event *event, int cpu) | |||
4489 | err = -ENOMEM; | 4494 | err = -ENOMEM; |
4490 | goto exit; | 4495 | goto exit; |
4491 | } | 4496 | } |
4492 | rcu_assign_pointer(cpuctx->swevent_hlist, hlist); | 4497 | rcu_assign_pointer(swhash->swevent_hlist, hlist); |
4493 | } | 4498 | } |
4494 | cpuctx->hlist_refcount++; | 4499 | swhash->hlist_refcount++; |
4495 | exit: | 4500 | exit: |
4496 | mutex_unlock(&cpuctx->hlist_mutex); | 4501 | mutex_unlock(&swhash->hlist_mutex); |
4497 | 4502 | ||
4498 | return err; | 4503 | return err; |
4499 | } | 4504 | } |
@@ -5889,12 +5894,15 @@ int perf_event_init_task(struct task_struct *child) | |||
5889 | 5894 | ||
5890 | static void __init perf_event_init_all_cpus(void) | 5895 | static void __init perf_event_init_all_cpus(void) |
5891 | { | 5896 | { |
5892 | int cpu; | ||
5893 | struct perf_cpu_context *cpuctx; | 5897 | struct perf_cpu_context *cpuctx; |
5898 | struct swevent_htable *swhash; | ||
5899 | int cpu; | ||
5894 | 5900 | ||
5895 | for_each_possible_cpu(cpu) { | 5901 | for_each_possible_cpu(cpu) { |
5902 | swhash = &per_cpu(swevent_htable, cpu); | ||
5903 | mutex_init(&swhash->hlist_mutex); | ||
5904 | |||
5896 | cpuctx = &per_cpu(perf_cpu_context, cpu); | 5905 | cpuctx = &per_cpu(perf_cpu_context, cpu); |
5897 | mutex_init(&cpuctx->hlist_mutex); | ||
5898 | __perf_event_init_context(&cpuctx->ctx, NULL); | 5906 | __perf_event_init_context(&cpuctx->ctx, NULL); |
5899 | } | 5907 | } |
5900 | } | 5908 | } |
@@ -5902,18 +5910,21 @@ static void __init perf_event_init_all_cpus(void) | |||
5902 | static void __cpuinit perf_event_init_cpu(int cpu) | 5910 | static void __cpuinit perf_event_init_cpu(int cpu) |
5903 | { | 5911 | { |
5904 | struct perf_cpu_context *cpuctx; | 5912 | struct perf_cpu_context *cpuctx; |
5913 | struct swevent_htable *swhash; | ||
5905 | 5914 | ||
5906 | cpuctx = &per_cpu(perf_cpu_context, cpu); | 5915 | cpuctx = &per_cpu(perf_cpu_context, cpu); |
5907 | 5916 | ||
5908 | mutex_lock(&cpuctx->hlist_mutex); | 5917 | swhash = &per_cpu(swevent_htable, cpu); |
5909 | if (cpuctx->hlist_refcount > 0) { | 5918 | |
5919 | mutex_lock(&swhash->hlist_mutex); | ||
5920 | if (swhash->hlist_refcount > 0) { | ||
5910 | struct swevent_hlist *hlist; | 5921 | struct swevent_hlist *hlist; |
5911 | 5922 | ||
5912 | hlist = kzalloc(sizeof(*hlist), GFP_KERNEL); | 5923 | hlist = kzalloc_node(sizeof(*hlist), GFP_KERNEL, cpu_to_node(cpu)); |
5913 | WARN_ON_ONCE(!hlist); | 5924 | WARN_ON(!hlist); |
5914 | rcu_assign_pointer(cpuctx->swevent_hlist, hlist); | 5925 | rcu_assign_pointer(swhash->swevent_hlist, hlist); |
5915 | } | 5926 | } |
5916 | mutex_unlock(&cpuctx->hlist_mutex); | 5927 | mutex_unlock(&swhash->hlist_mutex); |
5917 | } | 5928 | } |
5918 | 5929 | ||
5919 | #ifdef CONFIG_HOTPLUG_CPU | 5930 | #ifdef CONFIG_HOTPLUG_CPU |
@@ -5931,11 +5942,12 @@ static void __perf_event_exit_cpu(void *info) | |||
5931 | static void perf_event_exit_cpu(int cpu) | 5942 | static void perf_event_exit_cpu(int cpu) |
5932 | { | 5943 | { |
5933 | struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); | 5944 | struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); |
5945 | struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu); | ||
5934 | struct perf_event_context *ctx = &cpuctx->ctx; | 5946 | struct perf_event_context *ctx = &cpuctx->ctx; |
5935 | 5947 | ||
5936 | mutex_lock(&cpuctx->hlist_mutex); | 5948 | mutex_lock(&swhash->hlist_mutex); |
5937 | swevent_hlist_release(cpuctx); | 5949 | swevent_hlist_release(swhash); |
5938 | mutex_unlock(&cpuctx->hlist_mutex); | 5950 | mutex_unlock(&swhash->hlist_mutex); |
5939 | 5951 | ||
5940 | mutex_lock(&ctx->mutex); | 5952 | mutex_lock(&ctx->mutex); |
5941 | smp_call_function_single(cpu, __perf_event_exit_cpu, NULL, 1); | 5953 | smp_call_function_single(cpu, __perf_event_exit_cpu, NULL, 1); |