diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2016-03-20 14:59:02 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-03-31 04:30:36 -0400 |
commit | 49de0493e5f67a8023fa6fa5c89097c1f77de74e (patch) | |
tree | 28d03554fd4a8629da2156e52790c2d6b05158a6 | |
parent | 4b6e2571bf00019e016255ad62b56feb9f498db7 (diff) |
x86/perf/intel/cstate: Make cstate hotplug handling actually work
The current implementation aside of being an incomprehensible mess is broken.
# cat /sys/bus/event_source/devices/cstate_core/cpumask
0-17
That's on a quad socket machine with 72 physical cores! Qualitee stuff.
So it's not a surprise that event migration in case of CPU hotplug does not
work either.
# perf stat -e cstate_core/c6-residency/ -C 1 sleep 60 &
# echo 0 >/sys/devices/system/cpu/cpu1/online
Tracing cstate_pmu_event_update gives me:
[001] cstate_pmu_event_update <-event_sched_out
After the fix it properly moves the event:
[001] cstate_pmu_event_update <-event_sched_out
[073] cstate_pmu_event_update <-__perf_event_read
[073] cstate_pmu_event_update <-event_sched_out
The migration of pkg events does not work either. Not that I'm surprised.
I really could not be bothered to decode that loop mess and simply replaced it
by querying the proper cpumasks which give us the answer in a comprehensible
way.
This also requires to direct the event to the current active reader CPU in
cstate_pmu_event_init() otherwise the hotplug logic can't work.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
[ Added event->cpu < 0 test to not explode]
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Borislav Petkov <bp@suse.de>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Kan Liang <kan.liang@intel.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Vince Weaver <vincent.weaver@maine.edu>
Link: http://lkml.kernel.org/r/20160320185623.422519970@linutronix.de
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | arch/x86/events/intel/cstate.c | 122 |
1 files changed, 53 insertions, 69 deletions
diff --git a/arch/x86/events/intel/cstate.c b/arch/x86/events/intel/cstate.c index 7946c4231169..5c2f55fe142a 100644 --- a/arch/x86/events/intel/cstate.c +++ b/arch/x86/events/intel/cstate.c | |||
@@ -385,7 +385,7 @@ static ssize_t cstate_get_attr_cpumask(struct device *dev, | |||
385 | static int cstate_pmu_event_init(struct perf_event *event) | 385 | static int cstate_pmu_event_init(struct perf_event *event) |
386 | { | 386 | { |
387 | u64 cfg = event->attr.config; | 387 | u64 cfg = event->attr.config; |
388 | int ret = 0; | 388 | int cpu; |
389 | 389 | ||
390 | if (event->attr.type != event->pmu->type) | 390 | if (event->attr.type != event->pmu->type) |
391 | return -ENOENT; | 391 | return -ENOENT; |
@@ -400,26 +400,36 @@ static int cstate_pmu_event_init(struct perf_event *event) | |||
400 | event->attr.sample_period) /* no sampling */ | 400 | event->attr.sample_period) /* no sampling */ |
401 | return -EINVAL; | 401 | return -EINVAL; |
402 | 402 | ||
403 | if (event->cpu < 0) | ||
404 | return -EINVAL; | ||
405 | |||
403 | if (event->pmu == &cstate_core_pmu) { | 406 | if (event->pmu == &cstate_core_pmu) { |
404 | if (cfg >= PERF_CSTATE_CORE_EVENT_MAX) | 407 | if (cfg >= PERF_CSTATE_CORE_EVENT_MAX) |
405 | return -EINVAL; | 408 | return -EINVAL; |
406 | if (!core_msr[cfg].attr) | 409 | if (!core_msr[cfg].attr) |
407 | return -EINVAL; | 410 | return -EINVAL; |
408 | event->hw.event_base = core_msr[cfg].msr; | 411 | event->hw.event_base = core_msr[cfg].msr; |
412 | cpu = cpumask_any_and(&cstate_core_cpu_mask, | ||
413 | topology_sibling_cpumask(event->cpu)); | ||
409 | } else if (event->pmu == &cstate_pkg_pmu) { | 414 | } else if (event->pmu == &cstate_pkg_pmu) { |
410 | if (cfg >= PERF_CSTATE_PKG_EVENT_MAX) | 415 | if (cfg >= PERF_CSTATE_PKG_EVENT_MAX) |
411 | return -EINVAL; | 416 | return -EINVAL; |
412 | if (!pkg_msr[cfg].attr) | 417 | if (!pkg_msr[cfg].attr) |
413 | return -EINVAL; | 418 | return -EINVAL; |
414 | event->hw.event_base = pkg_msr[cfg].msr; | 419 | event->hw.event_base = pkg_msr[cfg].msr; |
415 | } else | 420 | cpu = cpumask_any_and(&cstate_pkg_cpu_mask, |
421 | topology_core_cpumask(event->cpu)); | ||
422 | } else { | ||
416 | return -ENOENT; | 423 | return -ENOENT; |
424 | } | ||
417 | 425 | ||
418 | /* must be done before validate_group */ | 426 | if (cpu >= nr_cpu_ids) |
427 | return -ENODEV; | ||
428 | |||
429 | event->cpu = cpu; | ||
419 | event->hw.config = cfg; | 430 | event->hw.config = cfg; |
420 | event->hw.idx = -1; | 431 | event->hw.idx = -1; |
421 | 432 | return 0; | |
422 | return ret; | ||
423 | } | 433 | } |
424 | 434 | ||
425 | static inline u64 cstate_pmu_read_counter(struct perf_event *event) | 435 | static inline u64 cstate_pmu_read_counter(struct perf_event *event) |
@@ -469,102 +479,76 @@ static int cstate_pmu_event_add(struct perf_event *event, int mode) | |||
469 | return 0; | 479 | return 0; |
470 | } | 480 | } |
471 | 481 | ||
482 | /* | ||
483 | * Check if exiting cpu is the designated reader. If so migrate the | ||
484 | * events when there is a valid target available | ||
485 | */ | ||
472 | static void cstate_cpu_exit(int cpu) | 486 | static void cstate_cpu_exit(int cpu) |
473 | { | 487 | { |
474 | int i, id, target; | 488 | unsigned int target; |
475 | 489 | ||
476 | /* cpu exit for cstate core */ | 490 | if (has_cstate_core && |
477 | if (has_cstate_core) { | 491 | cpumask_test_and_clear_cpu(cpu, &cstate_core_cpu_mask)) { |
478 | id = topology_core_id(cpu); | 492 | |
479 | target = -1; | 493 | target = cpumask_any_but(topology_sibling_cpumask(cpu), cpu); |
480 | 494 | /* Migrate events if there is a valid target */ | |
481 | for_each_online_cpu(i) { | 495 | if (target < nr_cpu_ids) { |
482 | if (i == cpu) | ||
483 | continue; | ||
484 | if (id == topology_core_id(i)) { | ||
485 | target = i; | ||
486 | break; | ||
487 | } | ||
488 | } | ||
489 | if (cpumask_test_and_clear_cpu(cpu, &cstate_core_cpu_mask) && target >= 0) | ||
490 | cpumask_set_cpu(target, &cstate_core_cpu_mask); | 496 | cpumask_set_cpu(target, &cstate_core_cpu_mask); |
491 | WARN_ON(cpumask_empty(&cstate_core_cpu_mask)); | ||
492 | if (target >= 0) | ||
493 | perf_pmu_migrate_context(&cstate_core_pmu, cpu, target); | 497 | perf_pmu_migrate_context(&cstate_core_pmu, cpu, target); |
498 | } | ||
494 | } | 499 | } |
495 | 500 | ||
496 | /* cpu exit for cstate pkg */ | 501 | if (has_cstate_pkg && |
497 | if (has_cstate_pkg) { | 502 | cpumask_test_and_clear_cpu(cpu, &cstate_pkg_cpu_mask)) { |
498 | id = topology_physical_package_id(cpu); | 503 | |
499 | target = -1; | 504 | target = cpumask_any_but(topology_core_cpumask(cpu), cpu); |
500 | 505 | /* Migrate events if there is a valid target */ | |
501 | for_each_online_cpu(i) { | 506 | if (target < nr_cpu_ids) { |
502 | if (i == cpu) | ||
503 | continue; | ||
504 | if (id == topology_physical_package_id(i)) { | ||
505 | target = i; | ||
506 | break; | ||
507 | } | ||
508 | } | ||
509 | if (cpumask_test_and_clear_cpu(cpu, &cstate_pkg_cpu_mask) && target >= 0) | ||
510 | cpumask_set_cpu(target, &cstate_pkg_cpu_mask); | 507 | cpumask_set_cpu(target, &cstate_pkg_cpu_mask); |
511 | WARN_ON(cpumask_empty(&cstate_pkg_cpu_mask)); | ||
512 | if (target >= 0) | ||
513 | perf_pmu_migrate_context(&cstate_pkg_pmu, cpu, target); | 508 | perf_pmu_migrate_context(&cstate_pkg_pmu, cpu, target); |
509 | } | ||
514 | } | 510 | } |
515 | } | 511 | } |
516 | 512 | ||
517 | static void cstate_cpu_init(int cpu) | 513 | static void cstate_cpu_init(int cpu) |
518 | { | 514 | { |
519 | int i, id; | 515 | unsigned int target; |
520 | 516 | ||
521 | /* cpu init for cstate core */ | 517 | /* |
522 | if (has_cstate_core) { | 518 | * If this is the first online thread of that core, set it in |
523 | id = topology_core_id(cpu); | 519 | * the core cpu mask as the designated reader. |
524 | for_each_cpu(i, &cstate_core_cpu_mask) { | 520 | */ |
525 | if (id == topology_core_id(i)) | 521 | target = cpumask_any_and(&cstate_core_cpu_mask, |
526 | break; | 522 | topology_sibling_cpumask(cpu)); |
527 | } | ||
528 | if (i >= nr_cpu_ids) | ||
529 | cpumask_set_cpu(cpu, &cstate_core_cpu_mask); | ||
530 | } | ||
531 | 523 | ||
532 | /* cpu init for cstate pkg */ | 524 | if (has_cstate_core && target >= nr_cpu_ids) |
533 | if (has_cstate_pkg) { | 525 | cpumask_set_cpu(cpu, &cstate_core_cpu_mask); |
534 | id = topology_physical_package_id(cpu); | 526 | |
535 | for_each_cpu(i, &cstate_pkg_cpu_mask) { | 527 | /* |
536 | if (id == topology_physical_package_id(i)) | 528 | * If this is the first online thread of that package, set it |
537 | break; | 529 | * in the package cpu mask as the designated reader. |
538 | } | 530 | */ |
539 | if (i >= nr_cpu_ids) | 531 | target = cpumask_any_and(&cstate_pkg_cpu_mask, |
540 | cpumask_set_cpu(cpu, &cstate_pkg_cpu_mask); | 532 | topology_core_cpumask(cpu)); |
541 | } | 533 | if (has_cstate_pkg && target >= nr_cpu_ids) |
534 | cpumask_set_cpu(cpu, &cstate_pkg_cpu_mask); | ||
542 | } | 535 | } |
543 | 536 | ||
544 | static int cstate_cpu_notifier(struct notifier_block *self, | 537 | static int cstate_cpu_notifier(struct notifier_block *self, |
545 | unsigned long action, void *hcpu) | 538 | unsigned long action, void *hcpu) |
546 | { | 539 | { |
547 | unsigned int cpu = (long)hcpu; | 540 | unsigned int cpu = (long)hcpu; |
548 | 541 | ||
549 | switch (action & ~CPU_TASKS_FROZEN) { | 542 | switch (action & ~CPU_TASKS_FROZEN) { |
550 | case CPU_UP_PREPARE: | ||
551 | break; | ||
552 | case CPU_STARTING: | 543 | case CPU_STARTING: |
553 | cstate_cpu_init(cpu); | 544 | cstate_cpu_init(cpu); |
554 | break; | 545 | break; |
555 | case CPU_UP_CANCELED: | ||
556 | case CPU_DYING: | ||
557 | break; | ||
558 | case CPU_ONLINE: | ||
559 | case CPU_DEAD: | ||
560 | break; | ||
561 | case CPU_DOWN_PREPARE: | 546 | case CPU_DOWN_PREPARE: |
562 | cstate_cpu_exit(cpu); | 547 | cstate_cpu_exit(cpu); |
563 | break; | 548 | break; |
564 | default: | 549 | default: |
565 | break; | 550 | break; |
566 | } | 551 | } |
567 | |||
568 | return NOTIFY_OK; | 552 | return NOTIFY_OK; |
569 | } | 553 | } |
570 | 554 | ||