diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2009-10-08 05:56:07 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-10-09 09:56:14 -0400 |
commit | fe9081cc9bdabb0be953a39ad977cea14e35bce5 (patch) | |
tree | e4b18495cbbade72da915b825dd39aef0cefd5d5 /arch | |
parent | b690081d4d3f6a23541493f1682835c3cd5c54a1 (diff) |
perf, x86: Add simple group validation
Refuse to add events when the group wouldn't fit onto the PMU
anymore.
Naive implementation.
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Stephane Eranian <eranian@gmail.com>
LKML-Reference: <1254911461.26976.239.camel@twins>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 90 |
1 files changed, 69 insertions, 21 deletions
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 9c758548a0e6..9961d845719d 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
@@ -114,7 +114,8 @@ struct x86_pmu { | |||
114 | u64 intel_ctrl; | 114 | u64 intel_ctrl; |
115 | void (*enable_bts)(u64 config); | 115 | void (*enable_bts)(u64 config); |
116 | void (*disable_bts)(void); | 116 | void (*disable_bts)(void); |
117 | int (*get_event_idx)(struct hw_perf_event *hwc); | 117 | int (*get_event_idx)(struct cpu_hw_events *cpuc, |
118 | struct hw_perf_event *hwc); | ||
118 | }; | 119 | }; |
119 | 120 | ||
120 | static struct x86_pmu x86_pmu __read_mostly; | 121 | static struct x86_pmu x86_pmu __read_mostly; |
@@ -523,7 +524,7 @@ static u64 intel_pmu_raw_event(u64 hw_event) | |||
523 | #define CORE_EVNTSEL_UNIT_MASK 0x0000FF00ULL | 524 | #define CORE_EVNTSEL_UNIT_MASK 0x0000FF00ULL |
524 | #define CORE_EVNTSEL_EDGE_MASK 0x00040000ULL | 525 | #define CORE_EVNTSEL_EDGE_MASK 0x00040000ULL |
525 | #define CORE_EVNTSEL_INV_MASK 0x00800000ULL | 526 | #define CORE_EVNTSEL_INV_MASK 0x00800000ULL |
526 | #define CORE_EVNTSEL_REG_MASK 0xFF000000ULL | 527 | #define CORE_EVNTSEL_REG_MASK 0xFF000000ULL |
527 | 528 | ||
528 | #define CORE_EVNTSEL_MASK \ | 529 | #define CORE_EVNTSEL_MASK \ |
529 | (CORE_EVNTSEL_EVENT_MASK | \ | 530 | (CORE_EVNTSEL_EVENT_MASK | \ |
@@ -1390,8 +1391,7 @@ static void amd_pmu_enable_event(struct hw_perf_event *hwc, int idx) | |||
1390 | x86_pmu_enable_event(hwc, idx); | 1391 | x86_pmu_enable_event(hwc, idx); |
1391 | } | 1392 | } |
1392 | 1393 | ||
1393 | static int | 1394 | static int fixed_mode_idx(struct hw_perf_event *hwc) |
1394 | fixed_mode_idx(struct perf_event *event, struct hw_perf_event *hwc) | ||
1395 | { | 1395 | { |
1396 | unsigned int hw_event; | 1396 | unsigned int hw_event; |
1397 | 1397 | ||
@@ -1424,9 +1424,9 @@ fixed_mode_idx(struct perf_event *event, struct hw_perf_event *hwc) | |||
1424 | /* | 1424 | /* |
1425 | * generic counter allocator: get next free counter | 1425 | * generic counter allocator: get next free counter |
1426 | */ | 1426 | */ |
1427 | static int gen_get_event_idx(struct hw_perf_event *hwc) | 1427 | static int |
1428 | gen_get_event_idx(struct cpu_hw_events *cpuc, struct hw_perf_event *hwc) | ||
1428 | { | 1429 | { |
1429 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
1430 | int idx; | 1430 | int idx; |
1431 | 1431 | ||
1432 | idx = find_first_zero_bit(cpuc->used_mask, x86_pmu.num_events); | 1432 | idx = find_first_zero_bit(cpuc->used_mask, x86_pmu.num_events); |
@@ -1436,16 +1436,16 @@ static int gen_get_event_idx(struct hw_perf_event *hwc) | |||
1436 | /* | 1436 | /* |
1437 | * intel-specific counter allocator: check event constraints | 1437 | * intel-specific counter allocator: check event constraints |
1438 | */ | 1438 | */ |
1439 | static int intel_get_event_idx(struct hw_perf_event *hwc) | 1439 | static int |
1440 | intel_get_event_idx(struct cpu_hw_events *cpuc, struct hw_perf_event *hwc) | ||
1440 | { | 1441 | { |
1441 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
1442 | const struct event_constraint *event_constraint; | 1442 | const struct event_constraint *event_constraint; |
1443 | int i, code; | 1443 | int i, code; |
1444 | 1444 | ||
1445 | if (!event_constraint) | 1445 | if (!event_constraint) |
1446 | goto skip; | 1446 | goto skip; |
1447 | 1447 | ||
1448 | code = hwc->config & 0xff; | 1448 | code = hwc->config & CORE_EVNTSEL_EVENT_MASK; |
1449 | 1449 | ||
1450 | for_each_event_constraint(event_constraint, event_constraint) { | 1450 | for_each_event_constraint(event_constraint, event_constraint) { |
1451 | if (code == event_constraint->code) { | 1451 | if (code == event_constraint->code) { |
@@ -1457,26 +1457,22 @@ static int intel_get_event_idx(struct hw_perf_event *hwc) | |||
1457 | } | 1457 | } |
1458 | } | 1458 | } |
1459 | skip: | 1459 | skip: |
1460 | return gen_get_event_idx(hwc); | 1460 | return gen_get_event_idx(cpuc, hwc); |
1461 | } | 1461 | } |
1462 | 1462 | ||
1463 | /* | 1463 | static int |
1464 | * Find a PMC slot for the freshly enabled / scheduled in event: | 1464 | x86_schedule_event(struct cpu_hw_events *cpuc, struct hw_perf_event *hwc) |
1465 | */ | ||
1466 | static int x86_pmu_enable(struct perf_event *event) | ||
1467 | { | 1465 | { |
1468 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
1469 | struct hw_perf_event *hwc = &event->hw; | ||
1470 | int idx; | 1466 | int idx; |
1471 | 1467 | ||
1472 | idx = fixed_mode_idx(event, hwc); | 1468 | idx = fixed_mode_idx(hwc); |
1473 | if (idx == X86_PMC_IDX_FIXED_BTS) { | 1469 | if (idx == X86_PMC_IDX_FIXED_BTS) { |
1474 | /* BTS is already occupied. */ | 1470 | /* BTS is already occupied. */ |
1475 | if (test_and_set_bit(idx, cpuc->used_mask)) | 1471 | if (test_and_set_bit(idx, cpuc->used_mask)) |
1476 | return -EAGAIN; | 1472 | return -EAGAIN; |
1477 | 1473 | ||
1478 | hwc->config_base = 0; | 1474 | hwc->config_base = 0; |
1479 | hwc->event_base = 0; | 1475 | hwc->event_base = 0; |
1480 | hwc->idx = idx; | 1476 | hwc->idx = idx; |
1481 | } else if (idx >= 0) { | 1477 | } else if (idx >= 0) { |
1482 | /* | 1478 | /* |
@@ -1499,17 +1495,33 @@ static int x86_pmu_enable(struct perf_event *event) | |||
1499 | /* Try to get the previous generic event again */ | 1495 | /* Try to get the previous generic event again */ |
1500 | if (idx == -1 || test_and_set_bit(idx, cpuc->used_mask)) { | 1496 | if (idx == -1 || test_and_set_bit(idx, cpuc->used_mask)) { |
1501 | try_generic: | 1497 | try_generic: |
1502 | idx = x86_pmu.get_event_idx(hwc); | 1498 | idx = x86_pmu.get_event_idx(cpuc, hwc); |
1503 | if (idx == -1) | 1499 | if (idx == -1) |
1504 | return -EAGAIN; | 1500 | return -EAGAIN; |
1505 | 1501 | ||
1506 | set_bit(idx, cpuc->used_mask); | 1502 | set_bit(idx, cpuc->used_mask); |
1507 | hwc->idx = idx; | 1503 | hwc->idx = idx; |
1508 | } | 1504 | } |
1509 | hwc->config_base = x86_pmu.eventsel; | 1505 | hwc->config_base = x86_pmu.eventsel; |
1510 | hwc->event_base = x86_pmu.perfctr; | 1506 | hwc->event_base = x86_pmu.perfctr; |
1511 | } | 1507 | } |
1512 | 1508 | ||
1509 | return idx; | ||
1510 | } | ||
1511 | |||
1512 | /* | ||
1513 | * Find a PMC slot for the freshly enabled / scheduled in event: | ||
1514 | */ | ||
1515 | static int x86_pmu_enable(struct perf_event *event) | ||
1516 | { | ||
1517 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
1518 | struct hw_perf_event *hwc = &event->hw; | ||
1519 | int idx; | ||
1520 | |||
1521 | idx = x86_schedule_event(cpuc, hwc); | ||
1522 | if (idx < 0) | ||
1523 | return idx; | ||
1524 | |||
1513 | perf_events_lapic_init(); | 1525 | perf_events_lapic_init(); |
1514 | 1526 | ||
1515 | x86_pmu.disable(hwc, idx); | 1527 | x86_pmu.disable(hwc, idx); |
@@ -2212,11 +2224,47 @@ static const struct pmu pmu = { | |||
2212 | .unthrottle = x86_pmu_unthrottle, | 2224 | .unthrottle = x86_pmu_unthrottle, |
2213 | }; | 2225 | }; |
2214 | 2226 | ||
2227 | static int | ||
2228 | validate_event(struct cpu_hw_events *cpuc, struct perf_event *event) | ||
2229 | { | ||
2230 | struct hw_perf_event fake_event = event->hw; | ||
2231 | |||
2232 | if (event->pmu != &pmu) | ||
2233 | return 0; | ||
2234 | |||
2235 | return x86_schedule_event(cpuc, &fake_event); | ||
2236 | } | ||
2237 | |||
2238 | static int validate_group(struct perf_event *event) | ||
2239 | { | ||
2240 | struct perf_event *sibling, *leader = event->group_leader; | ||
2241 | struct cpu_hw_events fake_pmu; | ||
2242 | |||
2243 | memset(&fake_pmu, 0, sizeof(fake_pmu)); | ||
2244 | |||
2245 | if (!validate_event(&fake_pmu, leader)) | ||
2246 | return -ENOSPC; | ||
2247 | |||
2248 | list_for_each_entry(sibling, &leader->sibling_list, group_entry) { | ||
2249 | if (!validate_event(&fake_pmu, sibling)) | ||
2250 | return -ENOSPC; | ||
2251 | } | ||
2252 | |||
2253 | if (!validate_event(&fake_pmu, event)) | ||
2254 | return -ENOSPC; | ||
2255 | |||
2256 | return 0; | ||
2257 | } | ||
2258 | |||
2215 | const struct pmu *hw_perf_event_init(struct perf_event *event) | 2259 | const struct pmu *hw_perf_event_init(struct perf_event *event) |
2216 | { | 2260 | { |
2217 | int err; | 2261 | int err; |
2218 | 2262 | ||
2219 | err = __hw_perf_event_init(event); | 2263 | err = __hw_perf_event_init(event); |
2264 | if (!err) { | ||
2265 | if (event->group_leader != event) | ||
2266 | err = validate_group(event); | ||
2267 | } | ||
2220 | if (err) { | 2268 | if (err) { |
2221 | if (event->destroy) | 2269 | if (event->destroy) |
2222 | event->destroy(event); | 2270 | event->destroy(event); |