aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/perf_counter.c
diff options
context:
space:
mode:
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 0fe22c916e29..eeb1b46cf707 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