diff options
author | Lin Ming <ming.m.lin@intel.com> | 2010-04-23 01:56:12 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-05-07 05:31:03 -0400 |
commit | 4d1c52b02d977d884abb21d0bbaba6b5d6bc8374 (patch) | |
tree | a2c5941951c0a793c50c93ef5de7bce701829f62 /arch/x86/kernel/cpu/perf_event.c | |
parent | 6bde9b6ce0127e2a56228a2071536d422be31336 (diff) |
perf, x86: implement group scheduling transactional APIs
Convert to the transactional PMU API and remove the duplication of
group_sched_in().
Reviewed-by: Stephane Eranian <eranian@google.com>
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: David Miller <davem@davemloft.net>
Cc: Paul Mackerras <paulus@samba.org>
LKML-Reference: <1272002172.5707.61.camel@minggr.sh.intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/cpu/perf_event.c')
-rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 180 |
1 files changed, 67 insertions, 113 deletions
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 27fa9eeed024..fd4db0db3708 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
@@ -110,6 +110,8 @@ struct cpu_hw_events { | |||
110 | u64 tags[X86_PMC_IDX_MAX]; | 110 | u64 tags[X86_PMC_IDX_MAX]; |
111 | struct perf_event *event_list[X86_PMC_IDX_MAX]; /* in enabled order */ | 111 | struct perf_event *event_list[X86_PMC_IDX_MAX]; /* in enabled order */ |
112 | 112 | ||
113 | unsigned int group_flag; | ||
114 | |||
113 | /* | 115 | /* |
114 | * Intel DebugStore bits | 116 | * Intel DebugStore bits |
115 | */ | 117 | */ |
@@ -961,6 +963,14 @@ static int x86_pmu_enable(struct perf_event *event) | |||
961 | if (n < 0) | 963 | if (n < 0) |
962 | return n; | 964 | return n; |
963 | 965 | ||
966 | /* | ||
967 | * If group events scheduling transaction was started, | ||
968 | * skip the schedulability test here, it will be peformed | ||
969 | * at commit time(->commit_txn) as a whole | ||
970 | */ | ||
971 | if (cpuc->group_flag & PERF_EVENT_TXN_STARTED) | ||
972 | goto out; | ||
973 | |||
964 | ret = x86_pmu.schedule_events(cpuc, n, assign); | 974 | ret = x86_pmu.schedule_events(cpuc, n, assign); |
965 | if (ret) | 975 | if (ret) |
966 | return ret; | 976 | return ret; |
@@ -970,6 +980,7 @@ static int x86_pmu_enable(struct perf_event *event) | |||
970 | */ | 980 | */ |
971 | memcpy(cpuc->assign, assign, n*sizeof(int)); | 981 | memcpy(cpuc->assign, assign, n*sizeof(int)); |
972 | 982 | ||
983 | out: | ||
973 | cpuc->n_events = n; | 984 | cpuc->n_events = n; |
974 | cpuc->n_added += n - n0; | 985 | cpuc->n_added += n - n0; |
975 | 986 | ||
@@ -1227,119 +1238,6 @@ x86_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) | |||
1227 | return &unconstrained; | 1238 | return &unconstrained; |
1228 | } | 1239 | } |
1229 | 1240 | ||
1230 | static int x86_event_sched_in(struct perf_event *event, | ||
1231 | struct perf_cpu_context *cpuctx) | ||
1232 | { | ||
1233 | int ret = 0; | ||
1234 | |||
1235 | event->state = PERF_EVENT_STATE_ACTIVE; | ||
1236 | event->oncpu = smp_processor_id(); | ||
1237 | event->tstamp_running += event->ctx->time - event->tstamp_stopped; | ||
1238 | |||
1239 | if (!is_x86_event(event)) | ||
1240 | ret = event->pmu->enable(event); | ||
1241 | |||
1242 | if (!ret && !is_software_event(event)) | ||
1243 | cpuctx->active_oncpu++; | ||
1244 | |||
1245 | if (!ret && event->attr.exclusive) | ||
1246 | cpuctx->exclusive = 1; | ||
1247 | |||
1248 | return ret; | ||
1249 | } | ||
1250 | |||
1251 | static void x86_event_sched_out(struct perf_event *event, | ||
1252 | struct perf_cpu_context *cpuctx) | ||
1253 | { | ||
1254 | event->state = PERF_EVENT_STATE_INACTIVE; | ||
1255 | event->oncpu = -1; | ||
1256 | |||
1257 | if (!is_x86_event(event)) | ||
1258 | event->pmu->disable(event); | ||
1259 | |||
1260 | event->tstamp_running -= event->ctx->time - event->tstamp_stopped; | ||
1261 | |||
1262 | if (!is_software_event(event)) | ||
1263 | cpuctx->active_oncpu--; | ||
1264 | |||
1265 | if (event->attr.exclusive || !cpuctx->active_oncpu) | ||
1266 | cpuctx->exclusive = 0; | ||
1267 | } | ||
1268 | |||
1269 | /* | ||
1270 | * Called to enable a whole group of events. | ||
1271 | * Returns 1 if the group was enabled, or -EAGAIN if it could not be. | ||
1272 | * Assumes the caller has disabled interrupts and has | ||
1273 | * frozen the PMU with hw_perf_save_disable. | ||
1274 | * | ||
1275 | * called with PMU disabled. If successful and return value 1, | ||
1276 | * then guaranteed to call perf_enable() and hw_perf_enable() | ||
1277 | */ | ||
1278 | int hw_perf_group_sched_in(struct perf_event *leader, | ||
1279 | struct perf_cpu_context *cpuctx, | ||
1280 | struct perf_event_context *ctx) | ||
1281 | { | ||
1282 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
1283 | struct perf_event *sub; | ||
1284 | int assign[X86_PMC_IDX_MAX]; | ||
1285 | int n0, n1, ret; | ||
1286 | |||
1287 | if (!x86_pmu_initialized()) | ||
1288 | return 0; | ||
1289 | |||
1290 | /* n0 = total number of events */ | ||
1291 | n0 = collect_events(cpuc, leader, true); | ||
1292 | if (n0 < 0) | ||
1293 | return n0; | ||
1294 | |||
1295 | ret = x86_pmu.schedule_events(cpuc, n0, assign); | ||
1296 | if (ret) | ||
1297 | return ret; | ||
1298 | |||
1299 | ret = x86_event_sched_in(leader, cpuctx); | ||
1300 | if (ret) | ||
1301 | return ret; | ||
1302 | |||
1303 | n1 = 1; | ||
1304 | list_for_each_entry(sub, &leader->sibling_list, group_entry) { | ||
1305 | if (sub->state > PERF_EVENT_STATE_OFF) { | ||
1306 | ret = x86_event_sched_in(sub, cpuctx); | ||
1307 | if (ret) | ||
1308 | goto undo; | ||
1309 | ++n1; | ||
1310 | } | ||
1311 | } | ||
1312 | /* | ||
1313 | * copy new assignment, now we know it is possible | ||
1314 | * will be used by hw_perf_enable() | ||
1315 | */ | ||
1316 | memcpy(cpuc->assign, assign, n0*sizeof(int)); | ||
1317 | |||
1318 | cpuc->n_events = n0; | ||
1319 | cpuc->n_added += n1; | ||
1320 | ctx->nr_active += n1; | ||
1321 | |||
1322 | /* | ||
1323 | * 1 means successful and events are active | ||
1324 | * This is not quite true because we defer | ||
1325 | * actual activation until hw_perf_enable() but | ||
1326 | * this way we* ensure caller won't try to enable | ||
1327 | * individual events | ||
1328 | */ | ||
1329 | return 1; | ||
1330 | undo: | ||
1331 | x86_event_sched_out(leader, cpuctx); | ||
1332 | n0 = 1; | ||
1333 | list_for_each_entry(sub, &leader->sibling_list, group_entry) { | ||
1334 | if (sub->state == PERF_EVENT_STATE_ACTIVE) { | ||
1335 | x86_event_sched_out(sub, cpuctx); | ||
1336 | if (++n0 == n1) | ||
1337 | break; | ||
1338 | } | ||
1339 | } | ||
1340 | return ret; | ||
1341 | } | ||
1342 | |||
1343 | #include "perf_event_amd.c" | 1241 | #include "perf_event_amd.c" |
1344 | #include "perf_event_p6.c" | 1242 | #include "perf_event_p6.c" |
1345 | #include "perf_event_p4.c" | 1243 | #include "perf_event_p4.c" |
@@ -1471,6 +1369,59 @@ static inline void x86_pmu_read(struct perf_event *event) | |||
1471 | x86_perf_event_update(event); | 1369 | x86_perf_event_update(event); |
1472 | } | 1370 | } |
1473 | 1371 | ||
1372 | /* | ||
1373 | * Start group events scheduling transaction | ||
1374 | * Set the flag to make pmu::enable() not perform the | ||
1375 | * schedulability test, it will be performed at commit time | ||
1376 | */ | ||
1377 | static void x86_pmu_start_txn(const struct pmu *pmu) | ||
1378 | { | ||
1379 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
1380 | |||
1381 | cpuc->group_flag |= PERF_EVENT_TXN_STARTED; | ||
1382 | } | ||
1383 | |||
1384 | /* | ||
1385 | * Stop group events scheduling transaction | ||
1386 | * Clear the flag and pmu::enable() will perform the | ||
1387 | * schedulability test. | ||
1388 | */ | ||
1389 | static void x86_pmu_cancel_txn(const struct pmu *pmu) | ||
1390 | { | ||
1391 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
1392 | |||
1393 | cpuc->group_flag &= ~PERF_EVENT_TXN_STARTED; | ||
1394 | } | ||
1395 | |||
1396 | /* | ||
1397 | * Commit group events scheduling transaction | ||
1398 | * Perform the group schedulability test as a whole | ||
1399 | * Return 0 if success | ||
1400 | */ | ||
1401 | static int x86_pmu_commit_txn(const struct pmu *pmu) | ||
1402 | { | ||
1403 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
1404 | int assign[X86_PMC_IDX_MAX]; | ||
1405 | int n, ret; | ||
1406 | |||
1407 | n = cpuc->n_events; | ||
1408 | |||
1409 | if (!x86_pmu_initialized()) | ||
1410 | return -EAGAIN; | ||
1411 | |||
1412 | ret = x86_pmu.schedule_events(cpuc, n, assign); | ||
1413 | if (ret) | ||
1414 | return ret; | ||
1415 | |||
1416 | /* | ||
1417 | * copy new assignment, now we know it is possible | ||
1418 | * will be used by hw_perf_enable() | ||
1419 | */ | ||
1420 | memcpy(cpuc->assign, assign, n*sizeof(int)); | ||
1421 | |||
1422 | return 0; | ||
1423 | } | ||
1424 | |||
1474 | static const struct pmu pmu = { | 1425 | static const struct pmu pmu = { |
1475 | .enable = x86_pmu_enable, | 1426 | .enable = x86_pmu_enable, |
1476 | .disable = x86_pmu_disable, | 1427 | .disable = x86_pmu_disable, |
@@ -1478,6 +1429,9 @@ static const struct pmu pmu = { | |||
1478 | .stop = x86_pmu_stop, | 1429 | .stop = x86_pmu_stop, |
1479 | .read = x86_pmu_read, | 1430 | .read = x86_pmu_read, |
1480 | .unthrottle = x86_pmu_unthrottle, | 1431 | .unthrottle = x86_pmu_unthrottle, |
1432 | .start_txn = x86_pmu_start_txn, | ||
1433 | .cancel_txn = x86_pmu_cancel_txn, | ||
1434 | .commit_txn = x86_pmu_commit_txn, | ||
1481 | }; | 1435 | }; |
1482 | 1436 | ||
1483 | /* | 1437 | /* |