diff options
-rw-r--r-- | arch/powerpc/kernel/perf_counter.c | 6 | ||||
-rw-r--r-- | include/linux/perf_counter.h | 15 | ||||
-rw-r--r-- | kernel/perf_counter.c | 76 |
3 files changed, 97 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/perf_counter.c b/arch/powerpc/kernel/perf_counter.c index d05651584d43..e4349281b07d 100644 --- a/arch/powerpc/kernel/perf_counter.c +++ b/arch/powerpc/kernel/perf_counter.c | |||
@@ -417,6 +417,8 @@ void hw_perf_restore(u64 disable) | |||
417 | atomic64_set(&counter->hw.prev_count, val); | 417 | atomic64_set(&counter->hw.prev_count, val); |
418 | counter->hw.idx = hwc_index[i] + 1; | 418 | counter->hw.idx = hwc_index[i] + 1; |
419 | write_pmc(counter->hw.idx, val); | 419 | write_pmc(counter->hw.idx, val); |
420 | if (counter->user_page) | ||
421 | perf_counter_update_userpage(counter); | ||
420 | } | 422 | } |
421 | mb(); | 423 | mb(); |
422 | cpuhw->mmcr[0] |= MMCR0_PMXE | MMCR0_FCECE; | 424 | cpuhw->mmcr[0] |= MMCR0_PMXE | MMCR0_FCECE; |
@@ -572,6 +574,8 @@ static void power_perf_disable(struct perf_counter *counter) | |||
572 | ppmu->disable_pmc(counter->hw.idx - 1, cpuhw->mmcr); | 574 | ppmu->disable_pmc(counter->hw.idx - 1, cpuhw->mmcr); |
573 | write_pmc(counter->hw.idx, 0); | 575 | write_pmc(counter->hw.idx, 0); |
574 | counter->hw.idx = 0; | 576 | counter->hw.idx = 0; |
577 | if (counter->user_page) | ||
578 | perf_counter_update_userpage(counter); | ||
575 | break; | 579 | break; |
576 | } | 580 | } |
577 | } | 581 | } |
@@ -698,6 +702,8 @@ static void record_and_restart(struct perf_counter *counter, long val, | |||
698 | write_pmc(counter->hw.idx, val); | 702 | write_pmc(counter->hw.idx, val); |
699 | atomic64_set(&counter->hw.prev_count, val); | 703 | atomic64_set(&counter->hw.prev_count, val); |
700 | atomic64_set(&counter->hw.period_left, left); | 704 | atomic64_set(&counter->hw.period_left, left); |
705 | if (counter->user_page) | ||
706 | perf_counter_update_userpage(counter); | ||
701 | 707 | ||
702 | /* | 708 | /* |
703 | * Finally record data if requested. | 709 | * Finally record data if requested. |
diff --git a/include/linux/perf_counter.h b/include/linux/perf_counter.h index 18dc17d0a61c..40b324e91bf6 100644 --- a/include/linux/perf_counter.h +++ b/include/linux/perf_counter.h | |||
@@ -143,6 +143,17 @@ struct perf_counter_hw_event { | |||
143 | #define PERF_COUNTER_IOC_ENABLE _IO('$', 0) | 143 | #define PERF_COUNTER_IOC_ENABLE _IO('$', 0) |
144 | #define PERF_COUNTER_IOC_DISABLE _IO('$', 1) | 144 | #define PERF_COUNTER_IOC_DISABLE _IO('$', 1) |
145 | 145 | ||
146 | /* | ||
147 | * Structure of the page that can be mapped via mmap | ||
148 | */ | ||
149 | struct perf_counter_mmap_page { | ||
150 | __u32 version; /* version number of this structure */ | ||
151 | __u32 compat_version; /* lowest version this is compat with */ | ||
152 | __u32 lock; /* seqlock for synchronization */ | ||
153 | __u32 index; /* hardware counter identifier */ | ||
154 | __s64 offset; /* add to hardware counter value */ | ||
155 | }; | ||
156 | |||
146 | #ifdef __KERNEL__ | 157 | #ifdef __KERNEL__ |
147 | /* | 158 | /* |
148 | * Kernel-internal data types and definitions: | 159 | * Kernel-internal data types and definitions: |
@@ -278,6 +289,9 @@ struct perf_counter { | |||
278 | int oncpu; | 289 | int oncpu; |
279 | int cpu; | 290 | int cpu; |
280 | 291 | ||
292 | /* pointer to page shared with userspace via mmap */ | ||
293 | unsigned long user_page; | ||
294 | |||
281 | /* read() / irq related data */ | 295 | /* read() / irq related data */ |
282 | wait_queue_head_t waitq; | 296 | wait_queue_head_t waitq; |
283 | /* optional: for NMIs */ | 297 | /* optional: for NMIs */ |
@@ -361,6 +375,7 @@ extern int perf_counter_task_enable(void); | |||
361 | extern int hw_perf_group_sched_in(struct perf_counter *group_leader, | 375 | extern int hw_perf_group_sched_in(struct perf_counter *group_leader, |
362 | struct perf_cpu_context *cpuctx, | 376 | struct perf_cpu_context *cpuctx, |
363 | struct perf_counter_context *ctx, int cpu); | 377 | struct perf_counter_context *ctx, int cpu); |
378 | extern void perf_counter_update_userpage(struct perf_counter *counter); | ||
364 | 379 | ||
365 | extern void perf_counter_output(struct perf_counter *counter, | 380 | extern void perf_counter_output(struct perf_counter *counter, |
366 | int nmi, struct pt_regs *regs); | 381 | int nmi, struct pt_regs *regs); |
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c index ce34bff07bda..d9cfd902140e 100644 --- a/kernel/perf_counter.c +++ b/kernel/perf_counter.c | |||
@@ -1177,6 +1177,7 @@ static int perf_release(struct inode *inode, struct file *file) | |||
1177 | mutex_unlock(&counter->mutex); | 1177 | mutex_unlock(&counter->mutex); |
1178 | mutex_unlock(&ctx->mutex); | 1178 | mutex_unlock(&ctx->mutex); |
1179 | 1179 | ||
1180 | free_page(counter->user_page); | ||
1180 | free_counter(counter); | 1181 | free_counter(counter); |
1181 | put_context(ctx); | 1182 | put_context(ctx); |
1182 | 1183 | ||
@@ -1346,12 +1347,87 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
1346 | return err; | 1347 | return err; |
1347 | } | 1348 | } |
1348 | 1349 | ||
1350 | void perf_counter_update_userpage(struct perf_counter *counter) | ||
1351 | { | ||
1352 | struct perf_counter_mmap_page *userpg; | ||
1353 | |||
1354 | if (!counter->user_page) | ||
1355 | return; | ||
1356 | userpg = (struct perf_counter_mmap_page *) counter->user_page; | ||
1357 | |||
1358 | ++userpg->lock; | ||
1359 | smp_wmb(); | ||
1360 | userpg->index = counter->hw.idx; | ||
1361 | userpg->offset = atomic64_read(&counter->count); | ||
1362 | if (counter->state == PERF_COUNTER_STATE_ACTIVE) | ||
1363 | userpg->offset -= atomic64_read(&counter->hw.prev_count); | ||
1364 | smp_wmb(); | ||
1365 | ++userpg->lock; | ||
1366 | } | ||
1367 | |||
1368 | static int perf_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | ||
1369 | { | ||
1370 | struct perf_counter *counter = vma->vm_file->private_data; | ||
1371 | |||
1372 | if (!counter->user_page) | ||
1373 | return VM_FAULT_SIGBUS; | ||
1374 | |||
1375 | vmf->page = virt_to_page(counter->user_page); | ||
1376 | get_page(vmf->page); | ||
1377 | return 0; | ||
1378 | } | ||
1379 | |||
1380 | static struct vm_operations_struct perf_mmap_vmops = { | ||
1381 | .fault = perf_mmap_fault, | ||
1382 | }; | ||
1383 | |||
1384 | static int perf_mmap(struct file *file, struct vm_area_struct *vma) | ||
1385 | { | ||
1386 | struct perf_counter *counter = file->private_data; | ||
1387 | unsigned long userpg; | ||
1388 | |||
1389 | if (!(vma->vm_flags & VM_SHARED) || (vma->vm_flags & VM_WRITE)) | ||
1390 | return -EINVAL; | ||
1391 | if (vma->vm_end - vma->vm_start != PAGE_SIZE) | ||
1392 | return -EINVAL; | ||
1393 | |||
1394 | /* | ||
1395 | * For now, restrict to the case of a hardware counter | ||
1396 | * on the current task. | ||
1397 | */ | ||
1398 | if (is_software_counter(counter) || counter->task != current) | ||
1399 | return -EINVAL; | ||
1400 | |||
1401 | userpg = counter->user_page; | ||
1402 | if (!userpg) { | ||
1403 | userpg = get_zeroed_page(GFP_KERNEL); | ||
1404 | mutex_lock(&counter->mutex); | ||
1405 | if (counter->user_page) { | ||
1406 | free_page(userpg); | ||
1407 | userpg = counter->user_page; | ||
1408 | } else { | ||
1409 | counter->user_page = userpg; | ||
1410 | } | ||
1411 | mutex_unlock(&counter->mutex); | ||
1412 | if (!userpg) | ||
1413 | return -ENOMEM; | ||
1414 | } | ||
1415 | |||
1416 | perf_counter_update_userpage(counter); | ||
1417 | |||
1418 | vma->vm_flags &= ~VM_MAYWRITE; | ||
1419 | vma->vm_flags |= VM_RESERVED; | ||
1420 | vma->vm_ops = &perf_mmap_vmops; | ||
1421 | return 0; | ||
1422 | } | ||
1423 | |||
1349 | static const struct file_operations perf_fops = { | 1424 | static const struct file_operations perf_fops = { |
1350 | .release = perf_release, | 1425 | .release = perf_release, |
1351 | .read = perf_read, | 1426 | .read = perf_read, |
1352 | .poll = perf_poll, | 1427 | .poll = perf_poll, |
1353 | .unlocked_ioctl = perf_ioctl, | 1428 | .unlocked_ioctl = perf_ioctl, |
1354 | .compat_ioctl = perf_ioctl, | 1429 | .compat_ioctl = perf_ioctl, |
1430 | .mmap = perf_mmap, | ||
1355 | }; | 1431 | }; |
1356 | 1432 | ||
1357 | /* | 1433 | /* |