aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/perf_counter.c
diff options
context:
space:
mode:
authorPeter Zijlstra <a.p.zijlstra@chello.nl>2009-03-25 07:30:22 -0400
committerIngo Molnar <mingo@elte.hu>2009-04-06 03:30:32 -0400
commitb9cacc7bf193df16532bfa7d7ca77fe50fc3c2e6 (patch)
tree4a8e4df2506876fe28e18c1e7b6e3ff21d0a9c6a /kernel/perf_counter.c
parentc7138f37f905bb7987b1f9f5a8ee73667db39f25 (diff)
perf_counter: more elaborate write API
Provide a begin, copy, end interface to the output buffer. begin() reserves the space, copy() copies the data over, considering page boundaries, end() finalizes the event and does the wakeup. Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> Cc: Mike Galbraith <efault@gmx.de> Cc: Arjan van de Ven <arjan@infradead.org> Cc: Wu Fengguang <fengguang.wu@intel.com> Orig-LKML-Reference: <20090325113316.740550870@chello.nl> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/perf_counter.c')
-rw-r--r--kernel/perf_counter.c109
1 files changed, 75 insertions, 34 deletions
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c
index affe227d56a0..0422fd9bf627 100644
--- a/kernel/perf_counter.c
+++ b/kernel/perf_counter.c
@@ -9,6 +9,7 @@
9 */ 9 */
10 10
11#include <linux/fs.h> 11#include <linux/fs.h>
12#include <linux/mm.h>
12#include <linux/cpu.h> 13#include <linux/cpu.h>
13#include <linux/smp.h> 14#include <linux/smp.h>
14#include <linux/file.h> 15#include <linux/file.h>
@@ -16,15 +17,14 @@
16#include <linux/sysfs.h> 17#include <linux/sysfs.h>
17#include <linux/ptrace.h> 18#include <linux/ptrace.h>
18#include <linux/percpu.h> 19#include <linux/percpu.h>
20#include <linux/vmstat.h>
21#include <linux/hardirq.h>
22#include <linux/rculist.h>
19#include <linux/uaccess.h> 23#include <linux/uaccess.h>
20#include <linux/syscalls.h> 24#include <linux/syscalls.h>
21#include <linux/anon_inodes.h> 25#include <linux/anon_inodes.h>
22#include <linux/kernel_stat.h> 26#include <linux/kernel_stat.h>
23#include <linux/perf_counter.h> 27#include <linux/perf_counter.h>
24#include <linux/mm.h>
25#include <linux/vmstat.h>
26#include <linux/rculist.h>
27#include <linux/hardirq.h>
28 28
29#include <asm/irq_regs.h> 29#include <asm/irq_regs.h>
30 30
@@ -1411,16 +1411,20 @@ static const struct file_operations perf_fops = {
1411 * Output 1411 * Output
1412 */ 1412 */
1413 1413
1414static int perf_output_write(struct perf_counter *counter, int nmi, 1414struct perf_output_handle {
1415 void *buf, ssize_t size) 1415 struct perf_counter *counter;
1416 struct perf_mmap_data *data;
1417 unsigned int offset;
1418 int wakeup;
1419};
1420
1421static int perf_output_begin(struct perf_output_handle *handle,
1422 struct perf_counter *counter, unsigned int size)
1416{ 1423{
1417 struct perf_mmap_data *data; 1424 struct perf_mmap_data *data;
1418 unsigned int offset, head, nr; 1425 unsigned int offset, head;
1419 unsigned int len;
1420 int ret, wakeup;
1421 1426
1422 rcu_read_lock(); 1427 rcu_read_lock();
1423 ret = -ENOSPC;
1424 data = rcu_dereference(counter->data); 1428 data = rcu_dereference(counter->data);
1425 if (!data) 1429 if (!data)
1426 goto out; 1430 goto out;
@@ -1428,45 +1432,82 @@ static int perf_output_write(struct perf_counter *counter, int nmi,
1428 if (!data->nr_pages) 1432 if (!data->nr_pages)
1429 goto out; 1433 goto out;
1430 1434
1431 ret = -EINVAL;
1432 if (size > PAGE_SIZE)
1433 goto out;
1434
1435 do { 1435 do {
1436 offset = head = atomic_read(&data->head); 1436 offset = head = atomic_read(&data->head);
1437 head += size; 1437 head += size;
1438 } while (atomic_cmpxchg(&data->head, offset, head) != offset); 1438 } while (atomic_cmpxchg(&data->head, offset, head) != offset);
1439 1439
1440 wakeup = (offset >> PAGE_SHIFT) != (head >> PAGE_SHIFT); 1440 handle->counter = counter;
1441 handle->data = data;
1442 handle->offset = offset;
1443 handle->wakeup = (offset >> PAGE_SHIFT) != (head >> PAGE_SHIFT);
1441 1444
1442 nr = (offset >> PAGE_SHIFT) & (data->nr_pages - 1); 1445 return 0;
1443 offset &= PAGE_SIZE - 1;
1444 1446
1445 len = min_t(unsigned int, PAGE_SIZE - offset, size); 1447out:
1446 memcpy(data->data_pages[nr] + offset, buf, len); 1448 rcu_read_unlock();
1447 size -= len;
1448 1449
1449 if (size) { 1450 return -ENOSPC;
1450 nr = (nr + 1) & (data->nr_pages - 1); 1451}
1451 memcpy(data->data_pages[nr], buf + len, size);
1452 }
1453 1452
1454 /* 1453static void perf_output_copy(struct perf_output_handle *handle,
1455 * generate a poll() wakeup for every page boundary crossed 1454 void *buf, unsigned int len)
1456 */ 1455{
1457 if (wakeup) { 1456 unsigned int pages_mask;
1458 atomic_xchg(&data->wakeup, POLL_IN); 1457 unsigned int offset;
1459 __perf_counter_update_userpage(counter, data); 1458 unsigned int size;
1459 void **pages;
1460
1461 offset = handle->offset;
1462 pages_mask = handle->data->nr_pages - 1;
1463 pages = handle->data->data_pages;
1464
1465 do {
1466 unsigned int page_offset;
1467 int nr;
1468
1469 nr = (offset >> PAGE_SHIFT) & pages_mask;
1470 page_offset = offset & (PAGE_SIZE - 1);
1471 size = min_t(unsigned int, PAGE_SIZE - page_offset, len);
1472
1473 memcpy(pages[nr] + page_offset, buf, size);
1474
1475 len -= size;
1476 buf += size;
1477 offset += size;
1478 } while (len);
1479
1480 handle->offset = offset;
1481}
1482
1483static void perf_output_end(struct perf_output_handle *handle, int nmi)
1484{
1485 if (handle->wakeup) {
1486 (void)atomic_xchg(&handle->data->wakeup, POLL_IN);
1487 __perf_counter_update_userpage(handle->counter, handle->data);
1460 if (nmi) { 1488 if (nmi) {
1461 counter->wakeup_pending = 1; 1489 handle->counter->wakeup_pending = 1;
1462 set_perf_counter_pending(); 1490 set_perf_counter_pending();
1463 } else 1491 } else
1464 wake_up(&counter->waitq); 1492 wake_up(&handle->counter->waitq);
1465 } 1493 }
1466 ret = 0;
1467out:
1468 rcu_read_unlock(); 1494 rcu_read_unlock();
1495}
1496
1497static int perf_output_write(struct perf_counter *counter, int nmi,
1498 void *buf, ssize_t size)
1499{
1500 struct perf_output_handle handle;
1501 int ret;
1469 1502
1503 ret = perf_output_begin(&handle, counter, size);
1504 if (ret)
1505 goto out;
1506
1507 perf_output_copy(&handle, buf, size);
1508 perf_output_end(&handle, nmi);
1509
1510out:
1470 return ret; 1511 return ret;
1471} 1512}
1472 1513