diff options
-rw-r--r-- | arch/powerpc/include/asm/perf_event_server.h | 1 | ||||
-rw-r--r-- | arch/powerpc/perf/core-book3s.c | 167 |
2 files changed, 165 insertions, 3 deletions
diff --git a/arch/powerpc/include/asm/perf_event_server.h b/arch/powerpc/include/asm/perf_event_server.h index 3f0c15c6f068..f265049dd7d6 100644 --- a/arch/powerpc/include/asm/perf_event_server.h +++ b/arch/powerpc/include/asm/perf_event_server.h | |||
@@ -73,6 +73,7 @@ extern int register_power_pmu(struct power_pmu *); | |||
73 | struct pt_regs; | 73 | struct pt_regs; |
74 | extern unsigned long perf_misc_flags(struct pt_regs *regs); | 74 | extern unsigned long perf_misc_flags(struct pt_regs *regs); |
75 | extern unsigned long perf_instruction_pointer(struct pt_regs *regs); | 75 | extern unsigned long perf_instruction_pointer(struct pt_regs *regs); |
76 | extern unsigned long int read_bhrb(int n); | ||
76 | 77 | ||
77 | /* | 78 | /* |
78 | * Only override the default definitions in include/linux/perf_event.h | 79 | * Only override the default definitions in include/linux/perf_event.h |
diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 4ac6e64a52ce..c627843c5b2e 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c | |||
@@ -19,6 +19,11 @@ | |||
19 | #include <asm/firmware.h> | 19 | #include <asm/firmware.h> |
20 | #include <asm/ptrace.h> | 20 | #include <asm/ptrace.h> |
21 | 21 | ||
22 | #define BHRB_MAX_ENTRIES 32 | ||
23 | #define BHRB_TARGET 0x0000000000000002 | ||
24 | #define BHRB_PREDICTION 0x0000000000000001 | ||
25 | #define BHRB_EA 0xFFFFFFFFFFFFFFFC | ||
26 | |||
22 | struct cpu_hw_events { | 27 | struct cpu_hw_events { |
23 | int n_events; | 28 | int n_events; |
24 | int n_percpu; | 29 | int n_percpu; |
@@ -38,7 +43,15 @@ struct cpu_hw_events { | |||
38 | 43 | ||
39 | unsigned int group_flag; | 44 | unsigned int group_flag; |
40 | int n_txn_start; | 45 | int n_txn_start; |
46 | |||
47 | /* BHRB bits */ | ||
48 | u64 bhrb_filter; /* BHRB HW branch filter */ | ||
49 | int bhrb_users; | ||
50 | void *bhrb_context; | ||
51 | struct perf_branch_stack bhrb_stack; | ||
52 | struct perf_branch_entry bhrb_entries[BHRB_MAX_ENTRIES]; | ||
41 | }; | 53 | }; |
54 | |||
42 | DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); | 55 | DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); |
43 | 56 | ||
44 | struct power_pmu *ppmu; | 57 | struct power_pmu *ppmu; |
@@ -858,6 +871,9 @@ static void power_pmu_enable(struct pmu *pmu) | |||
858 | } | 871 | } |
859 | 872 | ||
860 | out: | 873 | out: |
874 | if (cpuhw->bhrb_users) | ||
875 | ppmu->config_bhrb(cpuhw->bhrb_filter); | ||
876 | |||
861 | local_irq_restore(flags); | 877 | local_irq_restore(flags); |
862 | } | 878 | } |
863 | 879 | ||
@@ -888,6 +904,47 @@ static int collect_events(struct perf_event *group, int max_count, | |||
888 | return n; | 904 | return n; |
889 | } | 905 | } |
890 | 906 | ||
907 | /* Reset all possible BHRB entries */ | ||
908 | static void power_pmu_bhrb_reset(void) | ||
909 | { | ||
910 | asm volatile(PPC_CLRBHRB); | ||
911 | } | ||
912 | |||
913 | void power_pmu_bhrb_enable(struct perf_event *event) | ||
914 | { | ||
915 | struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); | ||
916 | |||
917 | if (!ppmu->bhrb_nr) | ||
918 | return; | ||
919 | |||
920 | /* Clear BHRB if we changed task context to avoid data leaks */ | ||
921 | if (event->ctx->task && cpuhw->bhrb_context != event->ctx) { | ||
922 | power_pmu_bhrb_reset(); | ||
923 | cpuhw->bhrb_context = event->ctx; | ||
924 | } | ||
925 | cpuhw->bhrb_users++; | ||
926 | } | ||
927 | |||
928 | void power_pmu_bhrb_disable(struct perf_event *event) | ||
929 | { | ||
930 | struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); | ||
931 | |||
932 | if (!ppmu->bhrb_nr) | ||
933 | return; | ||
934 | |||
935 | cpuhw->bhrb_users--; | ||
936 | WARN_ON_ONCE(cpuhw->bhrb_users < 0); | ||
937 | |||
938 | if (!cpuhw->disabled && !cpuhw->bhrb_users) { | ||
939 | /* BHRB cannot be turned off when other | ||
940 | * events are active on the PMU. | ||
941 | */ | ||
942 | |||
943 | /* avoid stale pointer */ | ||
944 | cpuhw->bhrb_context = NULL; | ||
945 | } | ||
946 | } | ||
947 | |||
891 | /* | 948 | /* |
892 | * Add a event to the PMU. | 949 | * Add a event to the PMU. |
893 | * If all events are not already frozen, then we disable and | 950 | * If all events are not already frozen, then we disable and |
@@ -947,6 +1004,9 @@ nocheck: | |||
947 | 1004 | ||
948 | ret = 0; | 1005 | ret = 0; |
949 | out: | 1006 | out: |
1007 | if (has_branch_stack(event)) | ||
1008 | power_pmu_bhrb_enable(event); | ||
1009 | |||
950 | perf_pmu_enable(event->pmu); | 1010 | perf_pmu_enable(event->pmu); |
951 | local_irq_restore(flags); | 1011 | local_irq_restore(flags); |
952 | return ret; | 1012 | return ret; |
@@ -999,6 +1059,9 @@ static void power_pmu_del(struct perf_event *event, int ef_flags) | |||
999 | cpuhw->mmcr[0] &= ~(MMCR0_PMXE | MMCR0_FCECE); | 1059 | cpuhw->mmcr[0] &= ~(MMCR0_PMXE | MMCR0_FCECE); |
1000 | } | 1060 | } |
1001 | 1061 | ||
1062 | if (has_branch_stack(event)) | ||
1063 | power_pmu_bhrb_disable(event); | ||
1064 | |||
1002 | perf_pmu_enable(event->pmu); | 1065 | perf_pmu_enable(event->pmu); |
1003 | local_irq_restore(flags); | 1066 | local_irq_restore(flags); |
1004 | } | 1067 | } |
@@ -1117,6 +1180,15 @@ int power_pmu_commit_txn(struct pmu *pmu) | |||
1117 | return 0; | 1180 | return 0; |
1118 | } | 1181 | } |
1119 | 1182 | ||
1183 | /* Called from ctxsw to prevent one process's branch entries to | ||
1184 | * mingle with the other process's entries during context switch. | ||
1185 | */ | ||
1186 | void power_pmu_flush_branch_stack(void) | ||
1187 | { | ||
1188 | if (ppmu->bhrb_nr) | ||
1189 | power_pmu_bhrb_reset(); | ||
1190 | } | ||
1191 | |||
1120 | /* | 1192 | /* |
1121 | * Return 1 if we might be able to put event on a limited PMC, | 1193 | * Return 1 if we might be able to put event on a limited PMC, |
1122 | * or 0 if not. | 1194 | * or 0 if not. |
@@ -1231,9 +1303,11 @@ static int power_pmu_event_init(struct perf_event *event) | |||
1231 | if (!ppmu) | 1303 | if (!ppmu) |
1232 | return -ENOENT; | 1304 | return -ENOENT; |
1233 | 1305 | ||
1234 | /* does not support taken branch sampling */ | 1306 | if (has_branch_stack(event)) { |
1235 | if (has_branch_stack(event)) | 1307 | /* PMU has BHRB enabled */ |
1236 | return -EOPNOTSUPP; | 1308 | if (!(ppmu->flags & PPMU_BHRB)) |
1309 | return -EOPNOTSUPP; | ||
1310 | } | ||
1237 | 1311 | ||
1238 | switch (event->attr.type) { | 1312 | switch (event->attr.type) { |
1239 | case PERF_TYPE_HARDWARE: | 1313 | case PERF_TYPE_HARDWARE: |
@@ -1314,6 +1388,15 @@ static int power_pmu_event_init(struct perf_event *event) | |||
1314 | 1388 | ||
1315 | cpuhw = &get_cpu_var(cpu_hw_events); | 1389 | cpuhw = &get_cpu_var(cpu_hw_events); |
1316 | err = power_check_constraints(cpuhw, events, cflags, n + 1); | 1390 | err = power_check_constraints(cpuhw, events, cflags, n + 1); |
1391 | |||
1392 | if (has_branch_stack(event)) { | ||
1393 | cpuhw->bhrb_filter = ppmu->bhrb_filter_map( | ||
1394 | event->attr.branch_sample_type); | ||
1395 | |||
1396 | if(cpuhw->bhrb_filter == -1) | ||
1397 | return -EOPNOTSUPP; | ||
1398 | } | ||
1399 | |||
1317 | put_cpu_var(cpu_hw_events); | 1400 | put_cpu_var(cpu_hw_events); |
1318 | if (err) | 1401 | if (err) |
1319 | return -EINVAL; | 1402 | return -EINVAL; |
@@ -1372,8 +1455,79 @@ struct pmu power_pmu = { | |||
1372 | .cancel_txn = power_pmu_cancel_txn, | 1455 | .cancel_txn = power_pmu_cancel_txn, |
1373 | .commit_txn = power_pmu_commit_txn, | 1456 | .commit_txn = power_pmu_commit_txn, |
1374 | .event_idx = power_pmu_event_idx, | 1457 | .event_idx = power_pmu_event_idx, |
1458 | .flush_branch_stack = power_pmu_flush_branch_stack, | ||
1375 | }; | 1459 | }; |
1376 | 1460 | ||
1461 | /* Processing BHRB entries */ | ||
1462 | void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw) | ||
1463 | { | ||
1464 | u64 val; | ||
1465 | u64 addr; | ||
1466 | int r_index, u_index, target, pred; | ||
1467 | |||
1468 | r_index = 0; | ||
1469 | u_index = 0; | ||
1470 | while (r_index < ppmu->bhrb_nr) { | ||
1471 | /* Assembly read function */ | ||
1472 | val = read_bhrb(r_index); | ||
1473 | |||
1474 | /* Terminal marker: End of valid BHRB entries */ | ||
1475 | if (val == 0) { | ||
1476 | break; | ||
1477 | } else { | ||
1478 | /* BHRB field break up */ | ||
1479 | addr = val & BHRB_EA; | ||
1480 | pred = val & BHRB_PREDICTION; | ||
1481 | target = val & BHRB_TARGET; | ||
1482 | |||
1483 | /* Probable Missed entry: Not applicable for POWER8 */ | ||
1484 | if ((addr == 0) && (target == 0) && (pred == 1)) { | ||
1485 | r_index++; | ||
1486 | continue; | ||
1487 | } | ||
1488 | |||
1489 | /* Real Missed entry: Power8 based missed entry */ | ||
1490 | if ((addr == 0) && (target == 1) && (pred == 1)) { | ||
1491 | r_index++; | ||
1492 | continue; | ||
1493 | } | ||
1494 | |||
1495 | /* Reserved condition: Not a valid entry */ | ||
1496 | if ((addr == 0) && (target == 1) && (pred == 0)) { | ||
1497 | r_index++; | ||
1498 | continue; | ||
1499 | } | ||
1500 | |||
1501 | /* Is a target address */ | ||
1502 | if (val & BHRB_TARGET) { | ||
1503 | /* First address cannot be a target address */ | ||
1504 | if (r_index == 0) { | ||
1505 | r_index++; | ||
1506 | continue; | ||
1507 | } | ||
1508 | |||
1509 | /* Update target address for the previous entry */ | ||
1510 | cpuhw->bhrb_entries[u_index - 1].to = addr; | ||
1511 | cpuhw->bhrb_entries[u_index - 1].mispred = pred; | ||
1512 | cpuhw->bhrb_entries[u_index - 1].predicted = ~pred; | ||
1513 | |||
1514 | /* Dont increment u_index */ | ||
1515 | r_index++; | ||
1516 | } else { | ||
1517 | /* Update address, flags for current entry */ | ||
1518 | cpuhw->bhrb_entries[u_index].from = addr; | ||
1519 | cpuhw->bhrb_entries[u_index].mispred = pred; | ||
1520 | cpuhw->bhrb_entries[u_index].predicted = ~pred; | ||
1521 | |||
1522 | /* Successfully popullated one entry */ | ||
1523 | u_index++; | ||
1524 | r_index++; | ||
1525 | } | ||
1526 | } | ||
1527 | } | ||
1528 | cpuhw->bhrb_stack.nr = u_index; | ||
1529 | return; | ||
1530 | } | ||
1377 | 1531 | ||
1378 | /* | 1532 | /* |
1379 | * A counter has overflowed; update its count and record | 1533 | * A counter has overflowed; update its count and record |
@@ -1433,6 +1587,13 @@ static void record_and_restart(struct perf_event *event, unsigned long val, | |||
1433 | if (event->attr.sample_type & PERF_SAMPLE_ADDR) | 1587 | if (event->attr.sample_type & PERF_SAMPLE_ADDR) |
1434 | perf_get_data_addr(regs, &data.addr); | 1588 | perf_get_data_addr(regs, &data.addr); |
1435 | 1589 | ||
1590 | if (event->attr.sample_type & PERF_SAMPLE_BRANCH_STACK) { | ||
1591 | struct cpu_hw_events *cpuhw; | ||
1592 | cpuhw = &__get_cpu_var(cpu_hw_events); | ||
1593 | power_pmu_bhrb_read(cpuhw); | ||
1594 | data.br_stack = &cpuhw->bhrb_stack; | ||
1595 | } | ||
1596 | |||
1436 | if (perf_event_overflow(event, &data, regs)) | 1597 | if (perf_event_overflow(event, &data, regs)) |
1437 | power_pmu_stop(event, 0); | 1598 | power_pmu_stop(event, 0); |
1438 | } | 1599 | } |