aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/cpu
diff options
context:
space:
mode:
authorLin Ming <ming.m.lin@intel.com>2010-04-23 01:56:12 -0400
committerIngo Molnar <mingo@elte.hu>2010-05-07 05:31:03 -0400
commit4d1c52b02d977d884abb21d0bbaba6b5d6bc8374 (patch)
treea2c5941951c0a793c50c93ef5de7bce701829f62 /arch/x86/kernel/cpu
parent6bde9b6ce0127e2a56228a2071536d422be31336 (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')
-rw-r--r--arch/x86/kernel/cpu/perf_event.c180
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
983out:
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
1230static 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
1251static 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 */
1278int 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;
1330undo:
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 */
1377static 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 */
1389static 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 */
1401static 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
1474static const struct pmu pmu = { 1425static 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/*