aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/perf_counter.c
diff options
context:
space:
mode:
authorPeter Zijlstra <a.p.zijlstra@chello.nl>2009-03-13 07:21:32 -0400
committerIngo Molnar <mingo@elte.hu>2009-04-06 03:29:36 -0400
commit15dbf27cc18559a14e99609f78678aa86b9c6ff1 (patch)
tree4b88cc01991cd994777c418c4420474588b5ce0d /kernel/perf_counter.c
parent82bae4f8c2fd64a2bb1e2e72c508853ed2b4a299 (diff)
perf_counter: software counter event infrastructure
Provide generic software counter infrastructure that supports software events. This will be used to allow sample based profiling based on software events such as pagefaults. The current infrastructure can only provide a count of such events, no place information. Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/perf_counter.c')
-rw-r--r--kernel/perf_counter.c201
1 files changed, 201 insertions, 0 deletions
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c
index 0fe22c916e2..eeb1b46cf70 100644
--- a/kernel/perf_counter.c
+++ b/kernel/perf_counter.c
@@ -1328,6 +1328,185 @@ static const struct file_operations perf_fops = {
1328 .compat_ioctl = perf_ioctl, 1328 .compat_ioctl = perf_ioctl,
1329}; 1329};
1330 1330
1331/*
1332 * Generic software counter infrastructure
1333 */
1334
1335static void perf_swcounter_update(struct perf_counter *counter)
1336{
1337 struct hw_perf_counter *hwc = &counter->hw;
1338 u64 prev, now;
1339 s64 delta;
1340
1341again:
1342 prev = atomic64_read(&hwc->prev_count);
1343 now = atomic64_read(&hwc->count);
1344 if (atomic64_cmpxchg(&hwc->prev_count, prev, now) != prev)
1345 goto again;
1346
1347 delta = now - prev;
1348
1349 atomic64_add(delta, &counter->count);
1350 atomic64_sub(delta, &hwc->period_left);
1351}
1352
1353static void perf_swcounter_set_period(struct perf_counter *counter)
1354{
1355 struct hw_perf_counter *hwc = &counter->hw;
1356 s64 left = atomic64_read(&hwc->period_left);
1357 s64 period = hwc->irq_period;
1358
1359 if (unlikely(left <= -period)) {
1360 left = period;
1361 atomic64_set(&hwc->period_left, left);
1362 }
1363
1364 if (unlikely(left <= 0)) {
1365 left += period;
1366 atomic64_add(period, &hwc->period_left);
1367 }
1368
1369 atomic64_set(&hwc->prev_count, -left);
1370 atomic64_set(&hwc->count, -left);
1371}
1372
1373static void perf_swcounter_save_and_restart(struct perf_counter *counter)
1374{
1375 perf_swcounter_update(counter);
1376 perf_swcounter_set_period(counter);
1377}
1378
1379static void perf_swcounter_store_irq(struct perf_counter *counter, u64 data)
1380{
1381 struct perf_data *irqdata = counter->irqdata;
1382
1383 if (irqdata->len > PERF_DATA_BUFLEN - sizeof(u64)) {
1384 irqdata->overrun++;
1385 } else {
1386 u64 *p = (u64 *) &irqdata->data[irqdata->len];
1387
1388 *p = data;
1389 irqdata->len += sizeof(u64);
1390 }
1391}
1392
1393static void perf_swcounter_handle_group(struct perf_counter *sibling)
1394{
1395 struct perf_counter *counter, *group_leader = sibling->group_leader;
1396
1397 list_for_each_entry(counter, &group_leader->sibling_list, list_entry) {
1398 perf_swcounter_update(counter);
1399 perf_swcounter_store_irq(sibling, counter->hw_event.type);
1400 perf_swcounter_store_irq(sibling, atomic64_read(&counter->count));
1401 }
1402}
1403
1404static void perf_swcounter_interrupt(struct perf_counter *counter,
1405 int nmi, struct pt_regs *regs)
1406{
1407 perf_swcounter_save_and_restart(counter);
1408
1409 switch (counter->hw_event.record_type) {
1410 case PERF_RECORD_SIMPLE:
1411 break;
1412
1413 case PERF_RECORD_IRQ:
1414 perf_swcounter_store_irq(counter, instruction_pointer(regs));
1415 break;
1416
1417 case PERF_RECORD_GROUP:
1418 perf_swcounter_handle_group(counter);
1419 break;
1420 }
1421
1422 if (nmi) {
1423 counter->wakeup_pending = 1;
1424 set_tsk_thread_flag(current, TIF_PERF_COUNTERS);
1425 } else
1426 wake_up(&counter->waitq);
1427}
1428
1429static int perf_swcounter_match(struct perf_counter *counter,
1430 enum hw_event_types event,
1431 struct pt_regs *regs)
1432{
1433 if (counter->state != PERF_COUNTER_STATE_ACTIVE)
1434 return 0;
1435
1436 if (counter->hw_event.raw)
1437 return 0;
1438
1439 if (counter->hw_event.type != event)
1440 return 0;
1441
1442 if (counter->hw_event.exclude_user && user_mode(regs))
1443 return 0;
1444
1445 if (counter->hw_event.exclude_kernel && !user_mode(regs))
1446 return 0;
1447
1448 return 1;
1449}
1450
1451static void perf_swcounter_ctx_event(struct perf_counter_context *ctx,
1452 enum hw_event_types event, u64 nr,
1453 int nmi, struct pt_regs *regs)
1454{
1455 struct perf_counter *counter;
1456 unsigned long flags;
1457 int neg;
1458
1459 if (list_empty(&ctx->counter_list))
1460 return;
1461
1462 spin_lock_irqsave(&ctx->lock, flags);
1463
1464 /*
1465 * XXX: make counter_list RCU safe
1466 */
1467 list_for_each_entry(counter, &ctx->counter_list, list_entry) {
1468 if (perf_swcounter_match(counter, event, regs)) {
1469 neg = atomic64_add_negative(nr, &counter->hw.count);
1470 if (counter->hw.irq_period && !neg)
1471 perf_swcounter_interrupt(counter, nmi, regs);
1472 }
1473 }
1474
1475 spin_unlock_irqrestore(&ctx->lock, flags);
1476}
1477
1478void perf_swcounter_event(enum hw_event_types event, u64 nr,
1479 int nmi, struct pt_regs *regs)
1480{
1481 struct perf_cpu_context *cpuctx = &get_cpu_var(perf_cpu_context);
1482
1483 perf_swcounter_ctx_event(&cpuctx->ctx, event, nr, nmi, regs);
1484 if (cpuctx->task_ctx)
1485 perf_swcounter_ctx_event(cpuctx->task_ctx, event, nr, nmi, regs);
1486
1487 put_cpu_var(perf_cpu_context);
1488}
1489
1490static void perf_swcounter_read(struct perf_counter *counter)
1491{
1492 perf_swcounter_update(counter);
1493}
1494
1495static int perf_swcounter_enable(struct perf_counter *counter)
1496{
1497 perf_swcounter_set_period(counter);
1498 return 0;
1499}
1500
1501static void perf_swcounter_disable(struct perf_counter *counter)
1502{
1503 perf_swcounter_update(counter);
1504}
1505
1506/*
1507 * Software counter: cpu wall time clock
1508 */
1509
1331static int cpu_clock_perf_counter_enable(struct perf_counter *counter) 1510static int cpu_clock_perf_counter_enable(struct perf_counter *counter)
1332{ 1511{
1333 int cpu = raw_smp_processor_id(); 1512 int cpu = raw_smp_processor_id();
@@ -1365,6 +1544,10 @@ static const struct hw_perf_counter_ops perf_ops_cpu_clock = {
1365}; 1544};
1366 1545
1367/* 1546/*
1547 * Software counter: task time clock
1548 */
1549
1550/*
1368 * Called from within the scheduler: 1551 * Called from within the scheduler:
1369 */ 1552 */
1370static u64 task_clock_perf_counter_val(struct perf_counter *counter, int update) 1553static u64 task_clock_perf_counter_val(struct perf_counter *counter, int update)
@@ -1420,6 +1603,10 @@ static const struct hw_perf_counter_ops perf_ops_task_clock = {
1420 .read = task_clock_perf_counter_read, 1603 .read = task_clock_perf_counter_read,
1421}; 1604};
1422 1605
1606/*
1607 * Software counter: page faults
1608 */
1609
1423#ifdef CONFIG_VM_EVENT_COUNTERS 1610#ifdef CONFIG_VM_EVENT_COUNTERS
1424#define cpu_page_faults() __get_cpu_var(vm_event_states).event[PGFAULT] 1611#define cpu_page_faults() __get_cpu_var(vm_event_states).event[PGFAULT]
1425#else 1612#else
@@ -1473,6 +1660,10 @@ static const struct hw_perf_counter_ops perf_ops_page_faults = {
1473 .read = page_faults_perf_counter_read, 1660 .read = page_faults_perf_counter_read,
1474}; 1661};
1475 1662
1663/*
1664 * Software counter: context switches
1665 */
1666
1476static u64 get_context_switches(struct perf_counter *counter) 1667static u64 get_context_switches(struct perf_counter *counter)
1477{ 1668{
1478 struct task_struct *curr = counter->ctx->task; 1669 struct task_struct *curr = counter->ctx->task;
@@ -1521,6 +1712,10 @@ static const struct hw_perf_counter_ops perf_ops_context_switches = {
1521 .read = context_switches_perf_counter_read, 1712 .read = context_switches_perf_counter_read,
1522}; 1713};
1523 1714
1715/*
1716 * Software counter: cpu migrations
1717 */
1718
1524static inline u64 get_cpu_migrations(struct perf_counter *counter) 1719static inline u64 get_cpu_migrations(struct perf_counter *counter)
1525{ 1720{
1526 struct task_struct *curr = counter->ctx->task; 1721 struct task_struct *curr = counter->ctx->task;
@@ -1572,7 +1767,9 @@ static const struct hw_perf_counter_ops perf_ops_cpu_migrations = {
1572static const struct hw_perf_counter_ops * 1767static const struct hw_perf_counter_ops *
1573sw_perf_counter_init(struct perf_counter *counter) 1768sw_perf_counter_init(struct perf_counter *counter)
1574{ 1769{
1770 struct perf_counter_hw_event *hw_event = &counter->hw_event;
1575 const struct hw_perf_counter_ops *hw_ops = NULL; 1771 const struct hw_perf_counter_ops *hw_ops = NULL;
1772 struct hw_perf_counter *hwc = &counter->hw;
1576 1773
1577 /* 1774 /*
1578 * Software counters (currently) can't in general distinguish 1775 * Software counters (currently) can't in general distinguish
@@ -1618,6 +1815,10 @@ sw_perf_counter_init(struct perf_counter *counter)
1618 default: 1815 default:
1619 break; 1816 break;
1620 } 1817 }
1818
1819 if (hw_ops)
1820 hwc->irq_period = hw_event->irq_period;
1821
1621 return hw_ops; 1822 return hw_ops;
1622} 1823}
1623 1824