linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] arm-ccn: fixes
@ 2016-08-11  9:50 Mark Rutland
  2016-08-11  9:50 ` [PATCH 1/3] arm-ccn: fix PMU interrupt flags Mark Rutland
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Mark Rutland @ 2016-08-11  9:50 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Pawel,

These patches fix a few issues I spotted in the ARM CCN driver.

I'd previously sent the first patch on its own, but as it wasn't picked up I've
rolled it up with the new patches.

I'm hoping that you will queue these with any other CCN driver updates. ;)

Thanks,
Mark.

Mark Rutland (3):
  arm-ccn: fix PMU interrupt flags
  arm-ccn: fix hrtimer registration
  arm-ccn: make event groups reliable

 drivers/bus/arm-ccn.c | 67 +++++++++++++++++++++++++++++++++------------------
 1 file changed, 44 insertions(+), 23 deletions(-)

-- 
1.9.1

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/3] arm-ccn: fix PMU interrupt flags
  2016-08-11  9:50 [PATCH 0/3] arm-ccn: fixes Mark Rutland
@ 2016-08-11  9:50 ` Mark Rutland
  2016-08-11  9:50 ` [PATCH 2/3] arm-ccn: fix hrtimer registration Mark Rutland
  2016-08-11  9:50 ` [PATCH 3/3] arm-ccn: make event groups reliable Mark Rutland
  2 siblings, 0 replies; 4+ messages in thread
From: Mark Rutland @ 2016-08-11  9:50 UTC (permalink / raw)
  To: linux-arm-kernel

Currently the IRQ core is permitted to make the CCN PMU IRQ handler
threaded, and will allow userspace to change the CPU affinity of the
interrupt behind our back. Both of these could violate our
synchronisation requirements with the core perf code, which relies upon
strict CPU affinity and disabling of interrupts to guarantee mutual
exclusion in some cases.

As with the CPU PMU drivers, we should request the interrupt with
IRQF_NOBALANCING and IRQF_NO_THREAD, to avoid these issues.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Acked-by: Pawel Moll <pawel.moll@arm.com>
Reviewed-by: Will Deacon <will.deacon@arm.com>
---
 drivers/bus/arm-ccn.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/bus/arm-ccn.c b/drivers/bus/arm-ccn.c
index cbb2771..28b1d66 100644
--- a/drivers/bus/arm-ccn.c
+++ b/drivers/bus/arm-ccn.c
@@ -1475,8 +1475,9 @@ static int arm_ccn_probe(struct platform_device *pdev)
 		/* Can set 'disable' bits, so can acknowledge interrupts */
 		writel(CCN_MN_ERRINT_STATUS__PMU_EVENTS__ENABLE,
 				ccn->base + CCN_MN_ERRINT_STATUS);
-		err = devm_request_irq(ccn->dev, irq, arm_ccn_irq_handler, 0,
-				dev_name(ccn->dev), ccn);
+		err = devm_request_irq(ccn->dev, irq, arm_ccn_irq_handler,
+				       IRQF_NOBALANCING | IRQF_NO_THREAD,
+				       dev_name(ccn->dev), ccn);
 		if (err)
 			return err;
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/3] arm-ccn: fix hrtimer registration
  2016-08-11  9:50 [PATCH 0/3] arm-ccn: fixes Mark Rutland
  2016-08-11  9:50 ` [PATCH 1/3] arm-ccn: fix PMU interrupt flags Mark Rutland
@ 2016-08-11  9:50 ` Mark Rutland
  2016-08-11  9:50 ` [PATCH 3/3] arm-ccn: make event groups reliable Mark Rutland
  2 siblings, 0 replies; 4+ messages in thread
From: Mark Rutland @ 2016-08-11  9:50 UTC (permalink / raw)
  To: linux-arm-kernel

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>
Cc: Pawel Moll <pawel.moll@arm.com>
---
 drivers/bus/arm-ccn.c | 33 +++++++++++++++++++++------------
 1 file changed, 21 insertions(+), 12 deletions(-)

diff --git a/drivers/bus/arm-ccn.c b/drivers/bus/arm-ccn.c
index 28b1d66..e07cd51b 100644
--- a/drivers/bus/arm-ccn.c
+++ b/drivers/bus/arm-ccn.c
@@ -921,15 +921,6 @@ static void arm_ccn_pmu_event_start(struct perf_event *event, int flags)
 			arm_ccn_pmu_read_counter(ccn, hw->idx));
 	hw->state = 0;
 
-	/*
-	 * Pin the timer, so that the overflows are handled by the chosen
-	 * event->cpu (this is the same one as presented in "cpumask"
-	 * attribute).
-	 */
-	if (!ccn->irq)
-		hrtimer_start(&ccn->dt.hrtimer, arm_ccn_pmu_timer_period(),
-				HRTIMER_MODE_REL_PINNED);
-
 	/* Set the DT bus input, engaging the counter */
 	arm_ccn_pmu_xp_dt_config(event, 1);
 }
@@ -943,9 +934,6 @@ static void arm_ccn_pmu_event_stop(struct perf_event *event, int flags)
 	/* Disable counting, setting the DT bus to pass-through mode */
 	arm_ccn_pmu_xp_dt_config(event, 0);
 
-	if (!ccn->irq)
-		hrtimer_cancel(&ccn->dt.hrtimer);
-
 	/* Let the DT bus drain */
 	timeout = arm_ccn_pmu_read_counter(ccn, CCN_IDX_PMU_CYCLE_COUNTER) +
 			ccn->num_xps;
@@ -1103,15 +1091,31 @@ static void arm_ccn_pmu_event_config(struct perf_event *event)
 	spin_unlock(&ccn->dt.config_lock);
 }
 
+static int arm_ccn_pmu_active_counters(struct arm_ccn *ccn)
+{
+	return bitmap_weight(ccn->dt.pmu_counters_mask,
+			     CCN_NUM_PMU_EVENT_COUNTERS + 1);
+}
+
 static int arm_ccn_pmu_event_add(struct perf_event *event, int flags)
 {
 	int err;
 	struct hw_perf_event *hw = &event->hw;
+	struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
 
 	err = arm_ccn_pmu_event_alloc(event);
 	if (err)
 		return err;
 
+	/*
+	 * Pin the timer, so that the overflows are handled by the chosen
+	 * event->cpu (this is the same one as presented in "cpumask"
+	 * attribute).
+	 */
+	if (!ccn->irq && arm_ccn_pmu_active_counters(ccn) == 1)
+		hrtimer_start(&ccn->dt.hrtimer, arm_ccn_pmu_timer_period(),
+			      HRTIMER_MODE_REL_PINNED);
+
 	arm_ccn_pmu_event_config(event);
 
 	hw->state = PERF_HES_STOPPED;
@@ -1124,9 +1128,14 @@ static int arm_ccn_pmu_event_add(struct perf_event *event, int flags)
 
 static void arm_ccn_pmu_event_del(struct perf_event *event, int flags)
 {
+	struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
+
 	arm_ccn_pmu_event_stop(event, PERF_EF_UPDATE);
 
 	arm_ccn_pmu_event_release(event);
+
+	if (!ccn->irq && arm_ccn_pmu_active_counters(ccn) == 0)
+		hrtimer_cancel(&ccn->dt.hrtimer);
 }
 
 static void arm_ccn_pmu_event_read(struct perf_event *event)
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 3/3] arm-ccn: make event groups reliable
  2016-08-11  9:50 [PATCH 0/3] arm-ccn: fixes Mark Rutland
  2016-08-11  9:50 ` [PATCH 1/3] arm-ccn: fix PMU interrupt flags Mark Rutland
  2016-08-11  9:50 ` [PATCH 2/3] arm-ccn: fix hrtimer registration Mark Rutland
@ 2016-08-11  9:50 ` Mark Rutland
  2 siblings, 0 replies; 4+ messages in thread
From: Mark Rutland @ 2016-08-11  9:50 UTC (permalink / raw)
  To: linux-arm-kernel

The CCN PMU driver leaves the counting logic always enabled, and thus
events are enabled while groups are manipulated. As each event is
stopped and read individually, this leads to arbitrary skew across event
groups, which can be seen if counting several identical events.

To avoid this, implement pmu_{enable,disable} callbacks to stop and
start all counters atomically around event manipulation. As the counters
are now stopped, we cannot poll the cycle counter to wait for events to
drain from the bus. However, as the counters are stopped and the events
will not be read regardless, we can simply allow the bus to drain
naturally.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Pawel Moll <pawel.moll@arm.com>
---
 drivers/bus/arm-ccn.c | 29 ++++++++++++++++++++---------
 1 file changed, 20 insertions(+), 9 deletions(-)

diff --git a/drivers/bus/arm-ccn.c b/drivers/bus/arm-ccn.c
index e07cd51b..4b4757f 100644
--- a/drivers/bus/arm-ccn.c
+++ b/drivers/bus/arm-ccn.c
@@ -927,20 +927,11 @@ static void arm_ccn_pmu_event_start(struct perf_event *event, int flags)
 
 static void arm_ccn_pmu_event_stop(struct perf_event *event, int flags)
 {
-	struct arm_ccn *ccn = pmu_to_arm_ccn(event->pmu);
 	struct hw_perf_event *hw = &event->hw;
-	u64 timeout;
 
 	/* Disable counting, setting the DT bus to pass-through mode */
 	arm_ccn_pmu_xp_dt_config(event, 0);
 
-	/* Let the DT bus drain */
-	timeout = arm_ccn_pmu_read_counter(ccn, CCN_IDX_PMU_CYCLE_COUNTER) +
-			ccn->num_xps;
-	while (arm_ccn_pmu_read_counter(ccn, CCN_IDX_PMU_CYCLE_COUNTER) <
-			timeout)
-		cpu_relax();
-
 	if (flags & PERF_EF_UPDATE)
 		arm_ccn_pmu_event_update(event);
 
@@ -1143,6 +1134,24 @@ static void arm_ccn_pmu_event_read(struct perf_event *event)
 	arm_ccn_pmu_event_update(event);
 }
 
+static void arm_ccn_pmu_enable(struct pmu *pmu)
+{
+	struct arm_ccn *ccn = pmu_to_arm_ccn(pmu);
+
+	u32 val = readl(ccn->dt.base + CCN_DT_PMCR);
+	val |= CCN_DT_PMCR__PMU_EN;
+	writel(val, ccn->dt.base + CCN_DT_PMCR);
+}
+
+static void arm_ccn_pmu_disable(struct pmu *pmu)
+{
+	struct arm_ccn *ccn = pmu_to_arm_ccn(pmu);
+
+	u32 val = readl(ccn->dt.base + CCN_DT_PMCR);
+	val &= ~CCN_DT_PMCR__PMU_EN;
+	writel(val, ccn->dt.base + CCN_DT_PMCR);
+}
+
 static irqreturn_t arm_ccn_pmu_overflow_handler(struct arm_ccn_dt *dt)
 {
 	u32 pmovsr = readl(dt->base + CCN_DT_PMOVSR);
@@ -1265,6 +1274,8 @@ static int arm_ccn_pmu_init(struct arm_ccn *ccn)
 		.start = arm_ccn_pmu_event_start,
 		.stop = arm_ccn_pmu_event_stop,
 		.read = arm_ccn_pmu_event_read,
+		.pmu_enable = arm_ccn_pmu_enable,
+		.pmu_disable = arm_ccn_pmu_disable,
 	};
 
 	/* No overflow interrupt? Have to use a timer instead. */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2016-08-11  9:50 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-08-11  9:50 [PATCH 0/3] arm-ccn: fixes Mark Rutland
2016-08-11  9:50 ` [PATCH 1/3] arm-ccn: fix PMU interrupt flags Mark Rutland
2016-08-11  9:50 ` [PATCH 2/3] arm-ccn: fix hrtimer registration Mark Rutland
2016-08-11  9:50 ` [PATCH 3/3] arm-ccn: make event groups reliable Mark Rutland

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).