LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH V2 0/9] genirq/timings: Fixes and selftests
@ 2019-05-24 11:16 Daniel Lezcano
  2019-05-24 11:16 ` [PATCH V2 1/9] genirq/timings: Fix next event index function Daniel Lezcano
                   ` (8 more replies)
  0 siblings, 9 replies; 15+ messages in thread
From: Daniel Lezcano @ 2019-05-24 11:16 UTC (permalink / raw)
  To: tglx; +Cc: linux-kernel, andriy.shevchenko

This series provides a couple of fixes, an optimization and the code
to do the selftests. 

While writing the selftests, a couple of issues were spotted with
the circular buffer handling and the routine searching for the pattern
multiple times.

In addition, a small optimization has been found while investigating the
bugs above.

In order to write the selftest, the routine needed by the core code and
the tests are wrapped into function which are always inline so the current
code is not impacted by a new function call. There is no functional
changes in this part.

Finally, the selftest uses samples to insert values in the arrays and
use them to predict the next event. These tests cover the most difficult
part of the code.

Changelog:

V2:
  - remove defaulting to 'n' as it is already the case

Daniel Lezcano (9):
  genirq/timings: Fix next event index function
  genirq/timings: Fix timings buffer inspection
  genirq/timings: Optimize the period detection speed
  genirq/timings: Use the min kernel macro
  genirq/timings: Encapsulate timings push
  genirq/timings: Encapsulate storing function
  genirq/timings: Add selftest for circular array
  genirq/timings: Add selftest for irqs circular buffer
  genirq/timings: Add selftest for next event computation

 kernel/irq/Makefile    |   3 +
 kernel/irq/internals.h |  21 +-
 kernel/irq/timings.c   | 458 +++++++++++++++++++++++++++++++++++++----
 lib/Kconfig.debug      |   8 +
 4 files changed, 445 insertions(+), 45 deletions(-)

-- 
2.17.1


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

* [PATCH V2 1/9] genirq/timings: Fix next event index function
  2019-05-24 11:16 [PATCH V2 0/9] genirq/timings: Fixes and selftests Daniel Lezcano
@ 2019-05-24 11:16 ` Daniel Lezcano
  2019-05-24 13:51   ` Andy Shevchenko
  2019-05-24 11:16 ` [PATCH V2 2/9] genirq/timings: Fix timings buffer inspection Daniel Lezcano
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 15+ messages in thread
From: Daniel Lezcano @ 2019-05-24 11:16 UTC (permalink / raw)
  To: tglx; +Cc: linux-kernel, andriy.shevchenko

The current code was luckily working with most of the interval samples
testing but actually it fails to correctly detect pattern repeatition
breaking at the end of the buffer.

Narrowing down the bug has been a real pain because of the pointers,
so the routine is rewrite by using indexes instead.

Fixes: bbba0e7c5cda "genirq/timings: Add array suffix computation code"
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 kernel/irq/timings.c | 53 ++++++++++++++++++++++++++++++++++++--------
 1 file changed, 44 insertions(+), 9 deletions(-)

diff --git a/kernel/irq/timings.c b/kernel/irq/timings.c
index 90c735da15d0..60362aca4ca4 100644
--- a/kernel/irq/timings.c
+++ b/kernel/irq/timings.c
@@ -297,7 +297,18 @@ static u64 irq_timings_ema_new(u64 value, u64 ema_old)
 
 static int irq_timings_next_event_index(int *buffer, size_t len, int period_max)
 {
-	int i;
+	int period;
+
+	/*
+	 * Move the beginnning pointer to the end minus the max period
+	 * x 3. We are at the point we can begin searching the pattern
+	 */
+	buffer = &buffer[len - (period_max * 3)];
+
+	/*
+	 * Adjust the length to the maximum allowed period x 3
+	 */
+	len = period_max * 3;
 
 	/*
 	 * The buffer contains the suite of intervals, in a ilog2
@@ -306,21 +317,45 @@ static int irq_timings_next_event_index(int *buffer, size_t len, int period_max)
 	 * period beginning at the end of the buffer. We do that for
 	 * each suffix.
 	 */
-	for (i = period_max; i >= PREDICTION_PERIOD_MIN ; i--) {
+	for (period = period_max; period >= PREDICTION_PERIOD_MIN ; period--) {
 
-		int *begin = &buffer[len - (i * 3)];
-		int *ptr = begin;
+		/*
+		 * The first comparison always succeed because the
+		 * suffix is deduced from the first n-period bytes of
+		 * the buffer and we compare the initial suffix with
+		 * itself, so we can skip the first iteration.
+		 */
+		int idx = period;
+		size_t size = period;
 
 		/*
 		 * We look if the suite with period 'i' repeat
 		 * itself. If it is truncated at the end, as it
 		 * repeats we can use the period to find out the next
-		 * element.
+		 * element with the modulo.
 		 */
-		while (!memcmp(ptr, begin, i * sizeof(*ptr))) {
-			ptr += i;
-			if (ptr >= &buffer[len])
-				return begin[((i * 3) % i)];
+		while (!memcmp(buffer, &buffer[idx], size * sizeof(int))) {
+
+			/*
+			 * Move the index in a period basis
+			 */
+			idx += size;
+
+			/*
+			 * If this condition is reached, all previous
+			 * memcmp were successful, so the period is
+			 * found.
+			 */
+			if (idx == len)
+				return buffer[len % period];
+
+			/*
+			 * If the remaining elements to compare are
+			 * smaller than the period, readjust the size
+			 * of the comparison for the last iteration.
+			 */
+			if (len - idx < period)
+				size = len - idx;
 		}
 	}
 
-- 
2.17.1


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

* [PATCH V2 2/9] genirq/timings: Fix timings buffer inspection
  2019-05-24 11:16 [PATCH V2 0/9] genirq/timings: Fixes and selftests Daniel Lezcano
  2019-05-24 11:16 ` [PATCH V2 1/9] genirq/timings: Fix next event index function Daniel Lezcano
@ 2019-05-24 11:16 ` Daniel Lezcano
  2019-05-24 11:16 ` [PATCH V2 3/9] genirq/timings: Optimize the period detection speed Daniel Lezcano
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Daniel Lezcano @ 2019-05-24 11:16 UTC (permalink / raw)
  To: tglx; +Cc: linux-kernel, andriy.shevchenko

It appears the index beginning computation is not correct, the current
code does:

     i = (irqts->count & IRQ_TIMINGS_MASK) - 1

If irqts->count is equal to zero, we end up with an index equal to -1,
but that does not happen because the function checks against zero
before and returns in such case.

However, if irqts->count is a multiple of IRQ_TIMINGS_SIZE, the
resulting & bit op will be zero and leads also to a -1 index.

Re-introduce the iteration loop belonging to the previous variance
code which was correct.

Fixes: bbba0e7c5cda "genirq/timings: Add array suffix computation code"
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 kernel/irq/timings.c | 23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/kernel/irq/timings.c b/kernel/irq/timings.c
index 60362aca4ca4..250bb00ccd85 100644
--- a/kernel/irq/timings.c
+++ b/kernel/irq/timings.c
@@ -267,6 +267,23 @@ void irq_timings_disable(void)
 #define PREDICTION_MAX		10 /* 2 ^ PREDICTION_MAX useconds */
 #define PREDICTION_BUFFER_SIZE	16 /* slots for EMAs, hardly more than 16 */
 
+/*
+ * Number of elements in the circular buffer: If it happens it was
+ * flushed before, then the number of elements could be smaller than
+ * IRQ_TIMINGS_SIZE, so the count is used, otherwise the array size is
+ * used as we wrapped. The index begins from zero when we did not
+ * wrap. That could be done in a nicer way with the proper circular
+ * array structure type but with the cost of extra computation in the
+ * interrupt handler hot path. We choose efficiency.
+ */
+#define for_each_irqts(i, irqts)					\
+	for (i = irqts->count < IRQ_TIMINGS_SIZE ?			\
+		     0 : irqts->count & IRQ_TIMINGS_MASK,		\
+		     irqts->count = min(IRQ_TIMINGS_SIZE,		\
+					irqts->count);			\
+	     irqts->count > 0; irqts->count--,				\
+		     i = (i + 1) & IRQ_TIMINGS_MASK)
+
 struct irqt_stat {
 	u64	last_ts;
 	u64	ema_time[PREDICTION_BUFFER_SIZE];
@@ -528,11 +545,7 @@ u64 irq_timings_next_event(u64 now)
 	 * model while decrementing the counter because we consume the
 	 * data from our circular buffer.
 	 */
-
-	i = (irqts->count & IRQ_TIMINGS_MASK) - 1;
-	irqts->count = min(IRQ_TIMINGS_SIZE, irqts->count);
-
-	for (; irqts->count > 0; irqts->count--, i = (i + 1) & IRQ_TIMINGS_MASK) {
+	for_each_irqts(i, irqts) {
 		irq = irq_timing_decode(irqts->values[i], &ts);
 		s = idr_find(&irqt_stats, irq);
 		if (s)
-- 
2.17.1


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

* [PATCH V2 3/9] genirq/timings: Optimize the period detection speed
  2019-05-24 11:16 [PATCH V2 0/9] genirq/timings: Fixes and selftests Daniel Lezcano
  2019-05-24 11:16 ` [PATCH V2 1/9] genirq/timings: Fix next event index function Daniel Lezcano
  2019-05-24 11:16 ` [PATCH V2 2/9] genirq/timings: Fix timings buffer inspection Daniel Lezcano
@ 2019-05-24 11:16 ` Daniel Lezcano
  2019-05-24 11:16 ` [PATCH V2 4/9] genirq/timings: Use the min kernel macro Daniel Lezcano
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Daniel Lezcano @ 2019-05-24 11:16 UTC (permalink / raw)
  To: tglx; +Cc: linux-kernel, andriy.shevchenko

If we have a minimal period and if there is a period which is a
multiple of it but lesser than the max period then it will be detected
before and the minimal period will be never reached.

 1 2 1 2 1 2 1 2 1 2 1 2
 <-----> <-----> <----->
 <-> <-> <-> <-> <-> <->

In our case, the minimum period is 2 and the maximum period is 5. That
means all repeating pattern of 2 will be detected as repeating pattern
of 4, it is pointless to go up to 2 when searching for the period as it
will always fail.

Remove one loop iteration by increasing the minimal period to 3.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 kernel/irq/timings.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/kernel/irq/timings.c b/kernel/irq/timings.c
index 250bb00ccd85..06bde5253a7d 100644
--- a/kernel/irq/timings.c
+++ b/kernel/irq/timings.c
@@ -261,7 +261,7 @@ void irq_timings_disable(void)
 #define EMA_ALPHA_VAL		64
 #define EMA_ALPHA_SHIFT		7
 
-#define PREDICTION_PERIOD_MIN	2
+#define PREDICTION_PERIOD_MIN	3
 #define PREDICTION_PERIOD_MAX	5
 #define PREDICTION_FACTOR	4
 #define PREDICTION_MAX		10 /* 2 ^ PREDICTION_MAX useconds */
-- 
2.17.1


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

* [PATCH V2 4/9] genirq/timings: Use the min kernel macro
  2019-05-24 11:16 [PATCH V2 0/9] genirq/timings: Fixes and selftests Daniel Lezcano
                   ` (2 preceding siblings ...)
  2019-05-24 11:16 ` [PATCH V2 3/9] genirq/timings: Optimize the period detection speed Daniel Lezcano
@ 2019-05-24 11:16 ` Daniel Lezcano
  2019-05-24 13:57   ` Andy Shevchenko
  2019-05-24 11:16 ` [PATCH V2 5/9] genirq/timings: Encapsulate timings push Daniel Lezcano
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 15+ messages in thread
From: Daniel Lezcano @ 2019-05-24 11:16 UTC (permalink / raw)
  To: tglx; +Cc: linux-kernel, andriy.shevchenko

The' min' is available as a kernel macro. Use it instead of writing
the same code.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 kernel/irq/timings.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/kernel/irq/timings.c b/kernel/irq/timings.c
index 06bde5253a7d..8928601b4b42 100644
--- a/kernel/irq/timings.c
+++ b/kernel/irq/timings.c
@@ -406,8 +406,7 @@ static u64 __irq_timings_next_event(struct irqt_stat *irqs, int irq, u64 now)
 	/*
 	 * 'count' will depends if the circular buffer wrapped or not
 	 */
-	count = irqs->count < IRQ_TIMINGS_SIZE ?
-		irqs->count : IRQ_TIMINGS_SIZE;
+	count = min_t(int, irqs->count,  IRQ_TIMINGS_SIZE);
 
 	start = irqs->count < IRQ_TIMINGS_SIZE ?
 		0 : (irqs->count & IRQ_TIMINGS_MASK);
-- 
2.17.1


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

* [PATCH V2 5/9] genirq/timings: Encapsulate timings push
  2019-05-24 11:16 [PATCH V2 0/9] genirq/timings: Fixes and selftests Daniel Lezcano
                   ` (3 preceding siblings ...)
  2019-05-24 11:16 ` [PATCH V2 4/9] genirq/timings: Use the min kernel macro Daniel Lezcano
@ 2019-05-24 11:16 ` Daniel Lezcano
  2019-05-24 11:16 ` [PATCH V2 6/9] genirq/timings: Encapsulate storing function Daniel Lezcano
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Daniel Lezcano @ 2019-05-24 11:16 UTC (permalink / raw)
  To: tglx; +Cc: linux-kernel, andriy.shevchenko

For the next patches providing the selftest, we do want to
artificially insert timings value in the circular buffer in order to
check the correctness of the code. Encapsulate the common code between
the future test code and the current with an always-inline tag.

No functional change.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 kernel/irq/internals.h | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
index 70c3053bc1f6..21f9927ff5ad 100644
--- a/kernel/irq/internals.h
+++ b/kernel/irq/internals.h
@@ -354,6 +354,16 @@ static inline int irq_timing_decode(u64 value, u64 *timestamp)
 	return value & U16_MAX;
 }
 
+static __always_inline void irq_timings_push(u64 ts, int irq)
+{
+	struct irq_timings *timings = this_cpu_ptr(&irq_timings);
+
+	timings->values[timings->count & IRQ_TIMINGS_MASK] =
+		irq_timing_encode(ts, irq);
+
+	timings->count++;
+}
+
 /*
  * The function record_irq_time is only called in one place in the
  * interrupts handler. We want this function always inline so the code
@@ -367,15 +377,8 @@ static __always_inline void record_irq_time(struct irq_desc *desc)
 	if (!static_branch_likely(&irq_timing_enabled))
 		return;
 
-	if (desc->istate & IRQS_TIMINGS) {
-		struct irq_timings *timings = this_cpu_ptr(&irq_timings);
-
-		timings->values[timings->count & IRQ_TIMINGS_MASK] =
-			irq_timing_encode(local_clock(),
-					  irq_desc_get_irq(desc));
-
-		timings->count++;
-	}
+	if (desc->istate & IRQS_TIMINGS)
+		irq_timings_push(local_clock(), irq_desc_get_irq(desc));
 }
 #else
 static inline void irq_remove_timings(struct irq_desc *desc) {}
-- 
2.17.1


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

* [PATCH V2 6/9] genirq/timings: Encapsulate storing function
  2019-05-24 11:16 [PATCH V2 0/9] genirq/timings: Fixes and selftests Daniel Lezcano
                   ` (4 preceding siblings ...)
  2019-05-24 11:16 ` [PATCH V2 5/9] genirq/timings: Encapsulate timings push Daniel Lezcano
@ 2019-05-24 11:16 ` Daniel Lezcano
  2019-05-24 11:16 ` [PATCH V2 7/9] genirq/timings: Add selftest for circular array Daniel Lezcano
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Daniel Lezcano @ 2019-05-24 11:16 UTC (permalink / raw)
  To: tglx; +Cc: linux-kernel, andriy.shevchenko

For the next patches providing the selftest, we want to insert
interval values directly in the buffer in order to check the
correctness of the code. Encapsulate the code doing that in a always
inline function in order to reuse it in the test code.

No functional changes.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 kernel/irq/timings.c | 53 ++++++++++++++++++++++++++++----------------
 1 file changed, 34 insertions(+), 19 deletions(-)

diff --git a/kernel/irq/timings.c b/kernel/irq/timings.c
index 8928601b4b42..02689e6168c4 100644
--- a/kernel/irq/timings.c
+++ b/kernel/irq/timings.c
@@ -431,11 +431,43 @@ static u64 __irq_timings_next_event(struct irqt_stat *irqs, int irq, u64 now)
 	return irqs->last_ts + irqs->ema_time[index];
 }
 
+static __always_inline int irq_timings_interval_index(u64 interval)
+{
+	/*
+	 * The PREDICTION_FACTOR increase the interval size for the
+	 * array of exponential average.
+	 */
+	u64 interval_us = (interval >> 10) / PREDICTION_FACTOR;
+
+	return likely(interval_us) ? ilog2(interval_us) : 0;
+}
+
+static __always_inline void __irq_timings_store(int irq, struct irqt_stat *irqs,
+						u64 interval)
+{
+	int index;
+
+	/*
+	 * Get the index in the ema table for this interrupt.
+	 */
+	index = irq_timings_interval_index(interval);
+
+	/*
+	 * Store the index as an element of the pattern in another
+	 * circular array.
+	 */
+	irqs->circ_timings[irqs->count & IRQ_TIMINGS_MASK] = index;
+
+	irqs->ema_time[index] = irq_timings_ema_new(interval,
+						    irqs->ema_time[index]);
+
+	irqs->count++;
+}
+
 static inline void irq_timings_store(int irq, struct irqt_stat *irqs, u64 ts)
 {
 	u64 old_ts = irqs->last_ts;
 	u64 interval;
-	int index;
 
 	/*
 	 * The timestamps are absolute time values, we need to compute
@@ -466,24 +498,7 @@ static inline void irq_timings_store(int irq, struct irqt_stat *irqs, u64 ts)
 		return;
 	}
 
-	/*
-	 * Get the index in the ema table for this interrupt. The
-	 * PREDICTION_FACTOR increase the interval size for the array
-	 * of exponential average.
-	 */
-	index = likely(interval) ?
-		ilog2((interval >> 10) / PREDICTION_FACTOR) : 0;
-
-	/*
-	 * Store the index as an element of the pattern in another
-	 * circular array.
-	 */
-	irqs->circ_timings[irqs->count & IRQ_TIMINGS_MASK] = index;
-
-	irqs->ema_time[index] = irq_timings_ema_new(interval,
-						    irqs->ema_time[index]);
-
-	irqs->count++;
+	__irq_timings_store(irq, irqs, interval);
 }
 
 /**
-- 
2.17.1


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

* [PATCH V2 7/9] genirq/timings: Add selftest for circular array
  2019-05-24 11:16 [PATCH V2 0/9] genirq/timings: Fixes and selftests Daniel Lezcano
                   ` (5 preceding siblings ...)
  2019-05-24 11:16 ` [PATCH V2 6/9] genirq/timings: Encapsulate storing function Daniel Lezcano
@ 2019-05-24 11:16 ` Daniel Lezcano
  2019-05-24 14:00   ` Andy Shevchenko
  2019-05-24 11:16 ` [PATCH V2 8/9] genirq/timings: Add selftest for irqs circular buffer Daniel Lezcano
  2019-05-24 11:16 ` [PATCH V2 9/9] genirq/timings: Add selftest for next event computation Daniel Lezcano
  8 siblings, 1 reply; 15+ messages in thread
From: Daniel Lezcano @ 2019-05-24 11:16 UTC (permalink / raw)
  To: tglx
  Cc: linux-kernel, andriy.shevchenko, Andrew Morton, Masahiro Yamada,
	Petr Mladek, Kees Cook, Matthew Wilcox, Joe Lawrence,
	Mikulas Patocka, Tetsuo Handa, Sri Krishna chowdary,
	Uladzislau Rezki (Sony),
	Changbin Du

Due to the complexity of the code and the difficulty to debug it,
let's add some selftests to the framework in order to spot issues or
regression at boot time when the runtime testing is enabled for this
subsystem.

This tests the circular buffer at the limits and validates:
 - the encoding / decoding of the values
 - the macro to browse the irq timings circular buffer
 - the function to push data in the circular buffer

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 kernel/irq/Makefile  |   3 ++
 kernel/irq/timings.c | 119 +++++++++++++++++++++++++++++++++++++++++++
 lib/Kconfig.debug    |   8 +++
 3 files changed, 130 insertions(+)

diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile
index ff6e352e3a6c..b4f53717d143 100644
--- a/kernel/irq/Makefile
+++ b/kernel/irq/Makefile
@@ -2,6 +2,9 @@
 
 obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o
 obj-$(CONFIG_IRQ_TIMINGS) += timings.o
+ifeq ($(CONFIG_TEST_IRQ_TIMINGS),y)
+	CFLAGS_timings.o += -DDEBUG
+endif
 obj-$(CONFIG_GENERIC_IRQ_CHIP) += generic-chip.o
 obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o
 obj-$(CONFIG_IRQ_DOMAIN) += irqdomain.o
diff --git a/kernel/irq/timings.c b/kernel/irq/timings.c
index 02689e6168c4..dae04117796c 100644
--- a/kernel/irq/timings.c
+++ b/kernel/irq/timings.c
@@ -1,10 +1,12 @@
 // SPDX-License-Identifier: GPL-2.0
 // Copyright (C) 2016, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
+#define pr_fmt(fmt) "irq_timings: " fmt
 
 #include <linux/kernel.h>
 #include <linux/percpu.h>
 #include <linux/slab.h>
 #include <linux/static_key.h>
+#include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/idr.h>
 #include <linux/irq.h>
@@ -626,3 +628,120 @@ int irq_timings_alloc(int irq)
 
 	return 0;
 }
+
+#ifdef CONFIG_TEST_IRQ_TIMINGS
+static int __init irq_timings_test_irqts(struct irq_timings *irqts,
+					 unsigned count)
+{
+	int start = count >= IRQ_TIMINGS_SIZE ? count - IRQ_TIMINGS_SIZE : 0;
+	int i, irq, oirq = 0xBEEF;
+	u64 ots = 0xDEAD, ts;
+
+	/*
+	 * Fill the circular buffer by using the dedicated function.
+	 */
+	for (i = 0; i < count; i++) {
+		pr_debug("%d: index=%d, ts=%llX irq=%X\n",
+			 i, i & IRQ_TIMINGS_MASK, ots + i, oirq + i);
+
+		irq_timings_push(ots + i, oirq + i);
+	}
+
+	/*
+	 * Compute the first elements values after the index wrapped
+	 * up or not.
+	 */
+	ots += start;
+	oirq += start;
+
+	/*
+	 * Test the circular buffer count is correct.
+	 */
+	pr_debug("---> Checking timings array count (%d) is right\n", count);
+	if (WARN_ON(irqts->count != count))
+		return -EINVAL;
+
+	/*
+	 * Test the macro allowing to browse all the irqts.
+	 */
+	pr_debug("---> Checking the for_each_irqts() macro\n");
+	for_each_irqts(i, irqts) {
+
+		irq = irq_timing_decode(irqts->values[i], &ts);
+
+		pr_debug("index=%d, ts=%llX / %llX, irq=%X / %X\n",
+			 i, ts, ots, irq, oirq);
+
+		if (WARN_ON(ts != ots || irq != oirq))
+			return -EINVAL;
+
+		ots++; oirq++;
+	}
+
+	/*
+	 * The circular buffer should have be flushed when browsed
+	 * with for_each_irqts
+	 */
+	pr_debug("---> Checking timings array is empty after browsing it\n");
+	if (WARN_ON(irqts->count))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int __init irq_timings_irqts_selftest(void)
+{
+	struct irq_timings *irqts = this_cpu_ptr(&irq_timings);
+	int i, ret;
+
+	/*
+	 * Test the circular buffer with different number of
+	 * elements. The purpose is to test at the limits (empty, half
+	 * full, full, wrapped with the cursor at the boundaries,
+	 * wrapped several times, etc ...
+	 */
+	int count[] = { 0,
+			IRQ_TIMINGS_SIZE >> 1,
+			IRQ_TIMINGS_SIZE,
+			IRQ_TIMINGS_SIZE + (IRQ_TIMINGS_SIZE >> 1),
+			2 * IRQ_TIMINGS_SIZE,
+			(2 * IRQ_TIMINGS_SIZE) + 3,
+	};
+
+	for (i = 0; i < ARRAY_SIZE(count); i++) {
+
+		pr_info("---> Checking the timings with %d/%d values\n",
+			count[i], IRQ_TIMINGS_SIZE);
+
+		ret = irq_timings_test_irqts(irqts, count[i]);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+static int __init irq_timings_selftest(void)
+{
+	int ret;
+
+	pr_info("------------------- selftest start -----------------\n");
+
+	/*
+	 * At this point, we don't except any subsystem to use the irq
+	 * timings but us, so it should not be enabled.
+	 */
+	if (static_branch_unlikely(&irq_timing_enabled)) {
+		pr_warn("irq timings already initialized, skipping selftest\n");
+		return 0;
+	}
+
+	ret = irq_timings_irqts_selftest();
+
+	pr_info("---------- selftest end with %s -----------\n",
+		ret ? "failure" : "success");
+
+	return ret;
+}
+early_initcall(irq_timings_selftest);
+#endif
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 0d9e81779e37..88e9f398ffd0 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1827,6 +1827,14 @@ config TEST_PARMAN
 
 	  If unsure, say N.
 
+config TEST_IRQ_TIMINGS
+	bool "IRQ timings selftest"
+	depends on IRQ_TIMINGS
+	help
+	  Enable this option to test the irq timings code on boot.
+
+	  If unsure, say N.
+
 config TEST_LKM
 	tristate "Test module loading with 'hello world' module"
 	depends on m
-- 
2.17.1


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

* [PATCH V2 8/9] genirq/timings: Add selftest for irqs circular buffer
  2019-05-24 11:16 [PATCH V2 0/9] genirq/timings: Fixes and selftests Daniel Lezcano
                   ` (6 preceding siblings ...)
  2019-05-24 11:16 ` [PATCH V2 7/9] genirq/timings: Add selftest for circular array Daniel Lezcano
@ 2019-05-24 11:16 ` Daniel Lezcano
  2019-05-24 11:16 ` [PATCH V2 9/9] genirq/timings: Add selftest for next event computation Daniel Lezcano
  8 siblings, 0 replies; 15+ messages in thread
From: Daniel Lezcano @ 2019-05-24 11:16 UTC (permalink / raw)
  To: tglx; +Cc: linux-kernel, andriy.shevchenko

After testing the per cpu interrupt circular event, we want to make
sure the per interrupt circular buffer usage is correct.

Add tests to validate the interrupt circular buffer.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 kernel/irq/timings.c | 139 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 139 insertions(+)

diff --git a/kernel/irq/timings.c b/kernel/irq/timings.c
index dae04117796c..5e4efac967fd 100644
--- a/kernel/irq/timings.c
+++ b/kernel/irq/timings.c
@@ -630,6 +630,141 @@ int irq_timings_alloc(int irq)
 }
 
 #ifdef CONFIG_TEST_IRQ_TIMINGS
+struct timings_intervals {
+	u64 *intervals;
+	size_t count;
+};
+
+/*
+ * Intervals are given in nanosecond base
+ */
+static u64 intervals0[] __initdata = {
+	10000, 50000, 200000, 500000,
+	10000, 50000, 200000, 500000,
+	10000, 50000, 200000, 500000,
+	10000, 50000, 200000, 500000,
+	10000, 50000, 200000, 500000,
+	10000, 50000, 200000, 500000,
+	10000, 50000, 200000, 500000,
+	10000, 50000, 200000, 500000,
+	10000, 50000, 200000,
+};
+
+static u64 intervals1[] __initdata = {
+	223947000, 1240000, 1384000, 1386000, 1386000,
+	217416000, 1236000, 1384000, 1386000, 1387000,
+	214719000, 1241000, 1386000, 1387000, 1384000,
+	213696000, 1234000, 1384000, 1386000, 1388000,
+	219904000, 1240000, 1385000, 1389000, 1385000,
+	212240000, 1240000, 1386000, 1386000, 1386000,
+	214415000, 1236000, 1384000, 1386000, 1387000,
+	214276000, 1234000,
+};
+
+static u64 intervals2[] __initdata = {
+	4000, 3000, 5000, 100000,
+	3000, 3000, 5000, 117000,
+	4000, 4000, 5000, 112000,
+	4000, 3000, 4000, 110000,
+	3000, 5000, 3000, 117000,
+	4000, 4000, 5000, 112000,
+	4000, 3000, 4000, 110000,
+	3000, 4000, 5000, 112000,
+	4000,
+};
+
+static u64 intervals3[] __initdata = {
+	1385000, 212240000, 1240000,
+	1386000, 214415000, 1236000,
+	1384000, 214276000, 1234000,
+	1386000, 214415000, 1236000,
+	1385000, 212240000, 1240000,
+	1386000, 214415000, 1236000,
+	1384000, 214276000, 1234000,
+	1386000, 214415000, 1236000,
+	1385000, 212240000, 1240000,
+};
+
+static u64 intervals4[] __initdata = {
+	10000, 50000, 10000, 50000,
+	10000, 50000, 10000, 50000,
+	10000, 50000, 10000, 50000,
+	10000, 50000, 10000, 50000,
+	10000, 50000, 10000, 50000,
+	10000, 50000, 10000, 50000,
+	10000, 50000, 10000, 50000,
+	10000, 50000, 10000, 50000,
+	10000,
+};
+
+static struct timings_intervals tis[] __initdata = {
+	{ intervals0, ARRAY_SIZE(intervals0) },
+	{ intervals1, ARRAY_SIZE(intervals1) },
+	{ intervals2, ARRAY_SIZE(intervals2) },
+	{ intervals3, ARRAY_SIZE(intervals3) },
+	{ intervals4, ARRAY_SIZE(intervals4) },
+};
+
+static int __init irq_timings_test_irqs(struct timings_intervals *ti)
+{
+	struct irqt_stat __percpu *s;
+	struct irqt_stat *irqs;
+	int i, index, ret, irq = 0xACE5;
+
+	ret = irq_timings_alloc(irq);
+	if (ret) {
+		pr_err("Failed to allocate irq timings\n");
+		return ret;
+	}
+
+	s = idr_find(&irqt_stats, irq);
+	if (!s) {
+		ret = -EIDRM;
+		goto out;
+	}
+
+	irqs = this_cpu_ptr(s);
+
+	for (i = 0; i < ti->count; i++) {
+
+		index = irq_timings_interval_index(ti->intervals[i]);
+		pr_debug("%d: interval=%llu ema_index=%d\n",
+			 i, ti->intervals[i], index);
+
+		__irq_timings_store(irq, irqs, ti->intervals[i]);
+		if (irqs->circ_timings[i & IRQ_TIMINGS_MASK] != index) {
+			pr_err("Failed to store in the circular buffer\n");
+			goto out;
+		}
+	}
+
+	if (irqs->count != ti->count) {
+		pr_err("Count differs\n");
+		goto out;
+	}
+
+	ret = 0;
+out:
+	irq_timings_free(irq);
+
+	return ret;
+}
+
+static int __init irq_timings_irqs_selftest(void)
+{
+	int i, ret;
+
+	for (i = 0; i < ARRAY_SIZE(tis); i++) {
+		pr_info("---> Injecting intervals number #%d (count=%zd)\n",
+			i, tis[i].count);
+		ret = irq_timings_test_irqs(&tis[i]);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
 static int __init irq_timings_test_irqts(struct irq_timings *irqts,
 					 unsigned count)
 {
@@ -737,7 +872,11 @@ static int __init irq_timings_selftest(void)
 	}
 
 	ret = irq_timings_irqts_selftest();
+	if (ret)
+		goto out;
 
+	ret = irq_timings_irqs_selftest();
+out:
 	pr_info("---------- selftest end with %s -----------\n",
 		ret ? "failure" : "success");
 
-- 
2.17.1


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

* [PATCH V2 9/9] genirq/timings: Add selftest for next event computation
  2019-05-24 11:16 [PATCH V2 0/9] genirq/timings: Fixes and selftests Daniel Lezcano
                   ` (7 preceding siblings ...)
  2019-05-24 11:16 ` [PATCH V2 8/9] genirq/timings: Add selftest for irqs circular buffer Daniel Lezcano
@ 2019-05-24 11:16 ` Daniel Lezcano
  8 siblings, 0 replies; 15+ messages in thread
From: Daniel Lezcano @ 2019-05-24 11:16 UTC (permalink / raw)
  To: tglx; +Cc: linux-kernel, andriy.shevchenko

The circular buffers are now validated at this point with the two
previous patches. The next interrupt index algorithm which is the
hardest part to validate can be validated with the tests provided with
this patch.

It uses the intervals stored in the arrays and insert all the values
except the last one. The next event computation must return the same
value as the last element we did not inserted.

Add tests for the next event index computation.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 kernel/irq/timings.c | 66 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/kernel/irq/timings.c b/kernel/irq/timings.c
index 5e4efac967fd..eb2bb1d1551f 100644
--- a/kernel/irq/timings.c
+++ b/kernel/irq/timings.c
@@ -705,6 +705,68 @@ static struct timings_intervals tis[] __initdata = {
 	{ intervals4, ARRAY_SIZE(intervals4) },
 };
 
+static int __init irq_timings_test_next_index(struct timings_intervals *ti)
+{
+	int _buffer[IRQ_TIMINGS_SIZE];
+	int buffer[IRQ_TIMINGS_SIZE];
+	int index, start, i, count, period_max;
+
+	count = ti->count - 1;
+
+	period_max = count > (3 * PREDICTION_PERIOD_MAX) ?
+		PREDICTION_PERIOD_MAX : count / 3;
+
+	/*
+	 * Inject all values except the last one which will be used
+	 * to compare with the next index result.
+	 */
+	pr_debug("index suite: ");
+
+	for (i = 0; i < count; i++) {
+		index = irq_timings_interval_index(ti->intervals[i]);
+		_buffer[i & IRQ_TIMINGS_MASK] = index;
+		pr_cont("%d ", index);
+	}
+
+	start = count < IRQ_TIMINGS_SIZE ? 0 :
+		count & IRQ_TIMINGS_MASK;
+
+	count = min_t(int, count, IRQ_TIMINGS_SIZE);
+
+	for (i = 0; i < count; i++) {
+		int index = (start + i) & IRQ_TIMINGS_MASK;
+		buffer[i] = _buffer[index];
+	}
+
+	index = irq_timings_next_event_index(buffer, count, period_max);
+	i = irq_timings_interval_index(ti->intervals[ti->count - 1]);
+
+	if (index != i) {
+		pr_err("Expected (%d) and computed (%d) next indexes differ\n",
+		       i, index);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int __init irq_timings_next_index_selftest(void)
+{
+	int i, ret;
+
+	for (i = 0; i < ARRAY_SIZE(tis); i++) {
+
+		pr_info("---> Injecting intervals number #%d (count=%zd)\n",
+			i, tis[i].count);
+
+		ret = irq_timings_test_next_index(&tis[i]);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
 static int __init irq_timings_test_irqs(struct timings_intervals *ti)
 {
 	struct irqt_stat __percpu *s;
@@ -876,6 +938,10 @@ static int __init irq_timings_selftest(void)
 		goto out;
 
 	ret = irq_timings_irqs_selftest();
+	if (ret)
+		goto out;
+
+	ret = irq_timings_next_index_selftest();
 out:
 	pr_info("---------- selftest end with %s -----------\n",
 		ret ? "failure" : "success");
-- 
2.17.1


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

* Re: [PATCH V2 1/9] genirq/timings: Fix next event index function
  2019-05-24 11:16 ` [PATCH V2 1/9] genirq/timings: Fix next event index function Daniel Lezcano
@ 2019-05-24 13:51   ` Andy Shevchenko
  0 siblings, 0 replies; 15+ messages in thread
From: Andy Shevchenko @ 2019-05-24 13:51 UTC (permalink / raw)
  To: Daniel Lezcano; +Cc: tglx, linux-kernel

On Fri, May 24, 2019 at 01:16:07PM +0200, Daniel Lezcano wrote:
> The current code was luckily working with most of the interval samples
> testing but actually it fails to correctly detect pattern repeatition
> breaking at the end of the buffer.
> 
> Narrowing down the bug has been a real pain because of the pointers,
> so the routine is rewrite by using indexes instead.

Minor spelling issues below.

> +	/*
> +	 * Move the beginnning pointer to the end minus the max period

Typo: beginning.

> +	 * x 3. We are at the point we can begin searching the pattern

"x 3." would be read better on the previous line.

> +	 */
> +	buffer = &buffer[len - (period_max * 3)];

> +	/*
> +	 * Adjust the length to the maximum allowed period x 3
> +	 */

One line?

> +	len = period_max * 3;

> -	for (i = period_max; i >= PREDICTION_PERIOD_MIN ; i--) {
> +	for (period = period_max; period >= PREDICTION_PERIOD_MIN ; period--) {

At the same time you may drop extra white space.

>  	}

-- 
With Best Regards,
Andy Shevchenko



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

* Re: [PATCH V2 4/9] genirq/timings: Use the min kernel macro
  2019-05-24 11:16 ` [PATCH V2 4/9] genirq/timings: Use the min kernel macro Daniel Lezcano
@ 2019-05-24 13:57   ` Andy Shevchenko
  2019-05-24 16:11     ` Daniel Lezcano
  0 siblings, 1 reply; 15+ messages in thread
From: Andy Shevchenko @ 2019-05-24 13:57 UTC (permalink / raw)
  To: Daniel Lezcano; +Cc: tglx, linux-kernel

On Fri, May 24, 2019 at 01:16:10PM +0200, Daniel Lezcano wrote:
> The' min' is available as a kernel macro. Use it instead of writing
> the same code.

While it's technically correct...

>  	/*
>  	 * 'count' will depends if the circular buffer wrapped or not
>  	 */
> -	count = irqs->count < IRQ_TIMINGS_SIZE ?
> -		irqs->count : IRQ_TIMINGS_SIZE;
> +	count = min_t(int, irqs->count,  IRQ_TIMINGS_SIZE);
>  
>  	start = irqs->count < IRQ_TIMINGS_SIZE ?
>  		0 : (irqs->count & IRQ_TIMINGS_MASK);

...looking to the context I would leave as is to have a pattern.

-- 
With Best Regards,
Andy Shevchenko



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

* Re: [PATCH V2 7/9] genirq/timings: Add selftest for circular array
  2019-05-24 11:16 ` [PATCH V2 7/9] genirq/timings: Add selftest for circular array Daniel Lezcano
@ 2019-05-24 14:00   ` Andy Shevchenko
  2019-05-24 16:00     ` Daniel Lezcano
  0 siblings, 1 reply; 15+ messages in thread
From: Andy Shevchenko @ 2019-05-24 14:00 UTC (permalink / raw)
  To: Daniel Lezcano
  Cc: tglx, linux-kernel, Andrew Morton, Masahiro Yamada, Petr Mladek,
	Kees Cook, Matthew Wilcox, Joe Lawrence, Mikulas Patocka,
	Tetsuo Handa, Sri Krishna chowdary, Uladzislau Rezki (Sony),
	Changbin Du

On Fri, May 24, 2019 at 01:16:13PM +0200, Daniel Lezcano wrote:
> Due to the complexity of the code and the difficulty to debug it,
> let's add some selftests to the framework in order to spot issues or
> regression at boot time when the runtime testing is enabled for this
> subsystem.
> 
> This tests the circular buffer at the limits and validates:
>  - the encoding / decoding of the values
>  - the macro to browse the irq timings circular buffer
>  - the function to push data in the circular buffer

Can it use kselftest infrastructure?


-- 
With Best Regards,
Andy Shevchenko



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

* Re: [PATCH V2 7/9] genirq/timings: Add selftest for circular array
  2019-05-24 14:00   ` Andy Shevchenko
@ 2019-05-24 16:00     ` Daniel Lezcano
  0 siblings, 0 replies; 15+ messages in thread
From: Daniel Lezcano @ 2019-05-24 16:00 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: tglx, linux-kernel, Andrew Morton, Masahiro Yamada, Petr Mladek,
	Kees Cook, Matthew Wilcox, Joe Lawrence, Mikulas Patocka,
	Tetsuo Handa, Sri Krishna chowdary, Uladzislau Rezki (Sony),
	Changbin Du

On 24/05/2019 16:00, Andy Shevchenko wrote:
> On Fri, May 24, 2019 at 01:16:13PM +0200, Daniel Lezcano wrote:
>> Due to the complexity of the code and the difficulty to debug it,
>> let's add some selftests to the framework in order to spot issues or
>> regression at boot time when the runtime testing is enabled for this
>> subsystem.
>>
>> This tests the circular buffer at the limits and validates:
>>  - the encoding / decoding of the values
>>  - the macro to browse the irq timings circular buffer
>>  - the function to push data in the circular buffer
> 
> Can it use kselftest infrastructure?

I don't think it is possible because it is 100% deep-internal test, no
cooperation with userspace.


-- 
 <http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog


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

* Re: [PATCH V2 4/9] genirq/timings: Use the min kernel macro
  2019-05-24 13:57   ` Andy Shevchenko
@ 2019-05-24 16:11     ` Daniel Lezcano
  0 siblings, 0 replies; 15+ messages in thread
From: Daniel Lezcano @ 2019-05-24 16:11 UTC (permalink / raw)
  To: Andy Shevchenko; +Cc: tglx, linux-kernel

On 24/05/2019 15:57, Andy Shevchenko wrote:
> On Fri, May 24, 2019 at 01:16:10PM +0200, Daniel Lezcano wrote:
>> The' min' is available as a kernel macro. Use it instead of writing
>> the same code.
> 
> While it's technically correct...
> 
>>  	/*
>>  	 * 'count' will depends if the circular buffer wrapped or not
>>  	 */
>> -	count = irqs->count < IRQ_TIMINGS_SIZE ?
>> -		irqs->count : IRQ_TIMINGS_SIZE;
>> +	count = min_t(int, irqs->count,  IRQ_TIMINGS_SIZE);
>>  
>>  	start = irqs->count < IRQ_TIMINGS_SIZE ?
>>  		0 : (irqs->count & IRQ_TIMINGS_MASK);
> 
> ...looking to the context I would leave as is to have a pattern.

Yes, you are right. I'll drop this patch.


-- 
 <http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog


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

end of thread, other threads:[~2019-05-24 16:11 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-05-24 11:16 [PATCH V2 0/9] genirq/timings: Fixes and selftests Daniel Lezcano
2019-05-24 11:16 ` [PATCH V2 1/9] genirq/timings: Fix next event index function Daniel Lezcano
2019-05-24 13:51   ` Andy Shevchenko
2019-05-24 11:16 ` [PATCH V2 2/9] genirq/timings: Fix timings buffer inspection Daniel Lezcano
2019-05-24 11:16 ` [PATCH V2 3/9] genirq/timings: Optimize the period detection speed Daniel Lezcano
2019-05-24 11:16 ` [PATCH V2 4/9] genirq/timings: Use the min kernel macro Daniel Lezcano
2019-05-24 13:57   ` Andy Shevchenko
2019-05-24 16:11     ` Daniel Lezcano
2019-05-24 11:16 ` [PATCH V2 5/9] genirq/timings: Encapsulate timings push Daniel Lezcano
2019-05-24 11:16 ` [PATCH V2 6/9] genirq/timings: Encapsulate storing function Daniel Lezcano
2019-05-24 11:16 ` [PATCH V2 7/9] genirq/timings: Add selftest for circular array Daniel Lezcano
2019-05-24 14:00   ` Andy Shevchenko
2019-05-24 16:00     ` Daniel Lezcano
2019-05-24 11:16 ` [PATCH V2 8/9] genirq/timings: Add selftest for irqs circular buffer Daniel Lezcano
2019-05-24 11:16 ` [PATCH V2 9/9] genirq/timings: Add selftest for next event computation Daniel Lezcano

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).