aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Ellerman <michael@ellerman.id.au>2013-06-28 04:15:11 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-07-25 17:07:22 -0400
commit8cf3478f19143d4e2ece4947603bff7dbd360a36 (patch)
treef62cc2b754648b3fa85dfcc290d749a9b1e32b3e
parenta9514fe520175d13cba23cf3c9dbba4df9691c86 (diff)
powerpc/perf: Rework disable logic in pmu_disable()
commit 378a6ee99e4a431ec84e4e61893445c041c93007 upstream. In pmu_disable() we disable the PMU by setting the FC (Freeze Counters) bit in MMCR0. In order to do this we have to read/modify/write MMCR0. It's possible that we read a value from MMCR0 which has PMAO (PMU Alert Occurred) set. When we write that value back it will cause an interrupt to occur. We will then end up in the PMU interrupt handler even though we are supposed to have just disabled the PMU. We can avoid this by making sure we never write PMAO back. We should not lose interrupts because when the PMU is re-enabled the overflowed values will cause another interrupt. We also reorder the clearing of SAMPLE_ENABLE so that is done after the PMU is frozen. Otherwise there is a small window between the clearing of SAMPLE_ENABLE and the setting of FC where we could take an interrupt and incorrectly see SAMPLE_ENABLE not set. This would for example change the logic in perf_read_regs(). Signed-off-by: Michael Ellerman <michael@ellerman.id.au> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--arch/powerpc/perf/core-book3s.c31
1 files changed, 19 insertions, 12 deletions
diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c
index 29c6482890c8..1ab306815ff3 100644
--- a/arch/powerpc/perf/core-book3s.c
+++ b/arch/powerpc/perf/core-book3s.c
@@ -75,6 +75,7 @@ static unsigned int freeze_events_kernel = MMCR0_FCS;
75 75
76#define MMCR0_FCHV 0 76#define MMCR0_FCHV 0
77#define MMCR0_PMCjCE MMCR0_PMCnCE 77#define MMCR0_PMCjCE MMCR0_PMCnCE
78#define MMCR0_PMAO 0
78 79
79#define SPRN_MMCRA SPRN_MMCR2 80#define SPRN_MMCRA SPRN_MMCR2
80#define MMCRA_SAMPLE_ENABLE 0 81#define MMCRA_SAMPLE_ENABLE 0
@@ -852,7 +853,7 @@ static void write_mmcr0(struct cpu_hw_events *cpuhw, unsigned long mmcr0)
852static void power_pmu_disable(struct pmu *pmu) 853static void power_pmu_disable(struct pmu *pmu)
853{ 854{
854 struct cpu_hw_events *cpuhw; 855 struct cpu_hw_events *cpuhw;
855 unsigned long flags; 856 unsigned long flags, val;
856 857
857 if (!ppmu) 858 if (!ppmu)
858 return; 859 return;
@@ -860,9 +861,6 @@ static void power_pmu_disable(struct pmu *pmu)
860 cpuhw = &__get_cpu_var(cpu_hw_events); 861 cpuhw = &__get_cpu_var(cpu_hw_events);
861 862
862 if (!cpuhw->disabled) { 863 if (!cpuhw->disabled) {
863 cpuhw->disabled = 1;
864 cpuhw->n_added = 0;
865
866 /* 864 /*
867 * Check if we ever enabled the PMU on this cpu. 865 * Check if we ever enabled the PMU on this cpu.
868 */ 866 */
@@ -872,6 +870,21 @@ static void power_pmu_disable(struct pmu *pmu)
872 } 870 }
873 871
874 /* 872 /*
873 * Set the 'freeze counters' bit, clear PMAO.
874 */
875 val = mfspr(SPRN_MMCR0);
876 val |= MMCR0_FC;
877 val &= ~MMCR0_PMAO;
878
879 /*
880 * The barrier is to make sure the mtspr has been
881 * executed and the PMU has frozen the events etc.
882 * before we return.
883 */
884 write_mmcr0(cpuhw, val);
885 mb();
886
887 /*
875 * Disable instruction sampling if it was enabled 888 * Disable instruction sampling if it was enabled
876 */ 889 */
877 if (cpuhw->mmcr[2] & MMCRA_SAMPLE_ENABLE) { 890 if (cpuhw->mmcr[2] & MMCRA_SAMPLE_ENABLE) {
@@ -880,14 +893,8 @@ static void power_pmu_disable(struct pmu *pmu)
880 mb(); 893 mb();
881 } 894 }
882 895
883 /* 896 cpuhw->disabled = 1;
884 * Set the 'freeze counters' bit. 897 cpuhw->n_added = 0;
885 * The barrier is to make sure the mtspr has been
886 * executed and the PMU has frozen the events
887 * before we return.
888 */
889 write_mmcr0(cpuhw, mfspr(SPRN_MMCR0) | MMCR0_FC);
890 mb();
891 } 898 }
892 local_irq_restore(flags); 899 local_irq_restore(flags);
893} 900}