aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Rutland <mark.rutland@arm.com>2016-08-11 05:50:42 -0400
committerPawel Moll <pawel.moll@arm.com>2016-08-26 04:16:13 -0400
commit5b1e01f3ce15d3a8f2af5d38cc31f0d5c3c11dae (patch)
tree1e1a1d6f3b2b1c48fe8e15bd4c08792ccee9821a
parent0811ef7e2f5470833a353426a6fbe0b845aea926 (diff)
bus: arm-ccn: fix hrtimer registration
The CCN PMU driver has a single hrtimer, used to simulate a periodic interrupt on systems where the overflow interrupt is not possible to use. The hrtimer is started when any event is started, and cancelled when any event is stopped. Thus, stopping a single event is sufficient to disable to hrtimer, and overflows (of other events) may be lost. To avoid this, this patch reworks the hrtimer start/cancel to only occur when the first event is added to a PMU, and the last event removed, making use of the existing bitmap counting active events. Signed-off-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Pawel Moll <pawel.moll@arm.com>
-rw-r--r--drivers/bus/arm-ccn.c33
1 files changed, 21 insertions, 12 deletions
diff --git a/drivers/bus/arm-ccn.c b/drivers/bus/arm-ccn.c
index c826bb286054..12c1fd1bc398 100644
--- a/drivers/bus/arm-ccn.c
+++ b/drivers/bus/arm-ccn.c
@@ -940,15 +940,6 @@ static void arm_ccn_pmu_event_start(struct perf_event *event, int flags)
940 arm_ccn_pmu_read_counter(ccn, hw->idx)); 940 arm_ccn_pmu_read_counter(ccn, hw->idx));
941 hw->state = 0; 941 hw->state = 0;
942 942
943 /*
944 * Pin the timer, so that the overflows are handled by the chosen
945 * event->cpu (this is the same one as presented in "cpumask"
946 * attribute).
947 */
948 if (!ccn->irq)
949 hrtimer_start(&ccn->dt.hrtimer, arm_ccn_pmu_timer_period(),
950 HRTIMER_MODE_REL_PINNED);
951
952 /* Set the DT bus input, engaging the counter */ 943 /* Set the DT bus input, engaging the counter */
953 arm_ccn_pmu_xp_dt_config(event, 1); 944 arm_ccn_pmu_xp_dt_config(event, 1);
954} 945}
@@ -962,9 +953,6 @@ static void arm_ccn_pmu_event_stop(struct perf_event *event, int flags)
962 /* Disable counting, setting the DT bus to pass-through mode */ 953 /* Disable counting, setting the DT bus to pass-through mode */
963 arm_ccn_pmu_xp_dt_config(event, 0); 954 arm_ccn_pmu_xp_dt_config(event, 0);
964 955
965 if (!ccn->irq)
966 hrtimer_cancel(&ccn->dt.hrtimer);
967
968 /* Let the DT bus drain */ 956 /* Let the DT bus drain */
969 timeout = arm_ccn_pmu_read_counter(ccn, CCN_IDX_PMU_CYCLE_COUNTER) + 957 timeout = arm_ccn_pmu_read_counter(ccn, CCN_IDX_PMU_CYCLE_COUNTER) +
970 ccn->num_xps; 958 ccn->num_xps;
@@ -1122,15 +1110,31 @@ static void arm_ccn_pmu_event_config(struct perf_event *event)
1122 spin_unlock(&ccn->dt.config_lock); 1110 spin_unlock(&ccn->dt.config_lock);
1123} 1111}
1124 1112
1113static int arm_ccn_pmu_active_counters(struct arm_ccn *ccn)
1114{
1115 return bitmap_weight(ccn->dt.pmu_counters_mask,
1116 CCN_NUM_PMU_EVENT_COUNTERS + 1);
1117}
1118
1125static int arm_ccn_pmu_event_add(struct perf_event *event, int flags) 1119static int arm_ccn_pmu_event_add(struct perf_event *event, int flags)
1126{ 1120{
1127 int err; 1121 int err;
1128 struct hw_perf_event *hw = &event->hw; 1122 struct hw_perf_event *hw = &event->hw;
1123 struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
1129 1124
1130 err = arm_ccn_pmu_event_alloc(event); 1125 err = arm_ccn_pmu_event_alloc(event);
1131 if (err) 1126 if (err)
1132 return err; 1127 return err;
1133 1128
1129 /*
1130 * Pin the timer, so that the overflows are handled by the chosen
1131 * event->cpu (this is the same one as presented in "cpumask"
1132 * attribute).
1133 */
1134 if (!ccn->irq && arm_ccn_pmu_active_counters(ccn) == 1)
1135 hrtimer_start(&ccn->dt.hrtimer, arm_ccn_pmu_timer_period(),
1136 HRTIMER_MODE_REL_PINNED);
1137
1134 arm_ccn_pmu_event_config(event); 1138 arm_ccn_pmu_event_config(event);
1135 1139
1136 hw->state = PERF_HES_STOPPED; 1140 hw->state = PERF_HES_STOPPED;
@@ -1143,9 +1147,14 @@ static int arm_ccn_pmu_event_add(struct perf_event *event, int flags)
1143 1147
1144static void arm_ccn_pmu_event_del(struct perf_event *event, int flags) 1148static void arm_ccn_pmu_event_del(struct perf_event *event, int flags)
1145{ 1149{
1150 struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
1151
1146 arm_ccn_pmu_event_stop(event, PERF_EF_UPDATE); 1152 arm_ccn_pmu_event_stop(event, PERF_EF_UPDATE);
1147 1153
1148 arm_ccn_pmu_event_release(event); 1154 arm_ccn_pmu_event_release(event);
1155
1156 if (!ccn->irq && arm_ccn_pmu_active_counters(ccn) == 0)
1157 hrtimer_cancel(&ccn->dt.hrtimer);
1149} 1158}
1150 1159
1151static void arm_ccn_pmu_event_read(struct perf_event *event) 1160static void arm_ccn_pmu_event_read(struct perf_event *event)