aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/powerpc/kernel/perf_counter.c6
-rw-r--r--include/linux/perf_counter.h15
-rw-r--r--kernel/perf_counter.c76
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 */
149struct 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);
361extern int hw_perf_group_sched_in(struct perf_counter *group_leader, 375extern 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);
378extern void perf_counter_update_userpage(struct perf_counter *counter);
364 379
365extern void perf_counter_output(struct perf_counter *counter, 380extern 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
1350void 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
1368static 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
1380static struct vm_operations_struct perf_mmap_vmops = {
1381 .fault = perf_mmap_fault,
1382};
1383
1384static 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
1349static const struct file_operations perf_fops = { 1424static 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/*