[PATCH 8/8] LinuxPPS: pps_gen_parport: Add check for bad clocksource.

From: Andrey Drobyshev
Date: Wed Feb 15 2017 - 09:32:21 EST


From: Nikita Edward Baruzdin <nebaruzdin@xxxxxxxxx>

This commit is supposed to resolve the issue with hard lockups on systems using
jiffies as their clock source. Namely, it sets limits on number of iterations
clock source may remain unchanged (i. e. not being updated for one reason or
another, as it is with jiffies clock source), and on unsuccessful
getnstimeofday() polls as well. In case limit is reached, we consider clock
source incompatible with this driver or unstable.

Considering this issue to be fixed, un-BROKEN pps_gen_parport.

For explanation of the problem see this thread on lkml:
https://lkml.org/lkml/2011/2/18/310

Signed-off-by: Nikita Edward Baruzdin <nebaruzdin@xxxxxxxxxxxxx>
Signed-off-by: Alexander GQ Gerasiov <gq@xxxxxxxxx>
---
drivers/pps/generators/Kconfig | 2 +-
drivers/pps/generators/pps_gen_parport.c | 32 ++++++++++++++++++++++++++++++++
2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/drivers/pps/generators/Kconfig b/drivers/pps/generators/Kconfig
index e4c4f3d..f3a73dd 100644
--- a/drivers/pps/generators/Kconfig
+++ b/drivers/pps/generators/Kconfig
@@ -6,7 +6,7 @@ comment "PPS generators support"

config PPS_GENERATOR_PARPORT
tristate "Parallel port PPS signal generator"
- depends on PARPORT && BROKEN
+ depends on PARPORT
help
If you say yes here you get support for a PPS signal generator which
utilizes STROBE pin of a parallel port to send PPS signals. It uses
diff --git a/drivers/pps/generators/pps_gen_parport.c b/drivers/pps/generators/pps_gen_parport.c
index 7739301..bcb53cd 100644
--- a/drivers/pps/generators/pps_gen_parport.c
+++ b/drivers/pps/generators/pps_gen_parport.c
@@ -53,6 +53,13 @@ MODULE_PARM_DESC(polarity,
"Signal is on the low level (0 - default) or on the high level (1).");
module_param(polarity, uint, 0);

+static unsigned int failure_iterations = 5;
+MODULE_PARM_DESC(failure_iterations,
+ "Number of iterations the clock source may remain unchanged.");
+module_param(failure_iterations, uint, 0);
+
+#define MAX_GETTIME_ATTEMPTS 100000
+
#define SAFETY_INTERVAL 3000 /* set the hrtimer earlier for safety (ns) */

/* internal per port structure */
@@ -79,6 +86,7 @@ static enum hrtimer_restart hrtimer_event(struct hrtimer *timer)
struct parport *port;
long lim, delta;
unsigned long flags;
+ unsigned int i;

/* We have to disable interrupts here. The idea is to prevent
* other interrupts on the same processor to introduce random
@@ -106,8 +114,18 @@ static enum hrtimer_restart hrtimer_event(struct hrtimer *timer)
}

/* busy loop until the time is right for an assert edge */
+ i = 0;
do {
getnstimeofday(&ts2);
+ i++;
+
+ /* Check if there are problems with clock source
+ * and prevent hard lockups.
+ */
+ if ((i >= failure_iterations &&
+ ts1.tv_sec == ts2.tv_sec &&
+ ts1.tv_nsec == ts2.tv_nsec) || i > MAX_GETTIME_ATTEMPTS)
+ goto error;
} while (expire_time.tv_sec == ts2.tv_sec && ts2.tv_nsec < lim);

/* set the signal */
@@ -116,8 +134,17 @@ static enum hrtimer_restart hrtimer_event(struct hrtimer *timer)

/* busy loop until the time is right for a clear edge */
lim = NSEC_PER_SEC - dev->port_write_time;
+ i = 0;
do {
getnstimeofday(&ts2);
+ i++;
+
+ /* Check if there are problems with clock source
+ * and prevent hard lockups.
+ */
+ if (i > MAX_GETTIME_ATTEMPTS)
+ goto error;
+
} while (expire_time.tv_sec == ts2.tv_sec && ts2.tv_nsec < lim);

/* unset the signal */
@@ -154,6 +181,11 @@ static enum hrtimer_restart hrtimer_event(struct hrtimer *timer)
2 * hrtimer_error)));

return HRTIMER_RESTART;
+
+error:
+ local_irq_restore(flags);
+ pr_err("Clocksource unstable or not compatible with pps_gen_parport.");
+ return HRTIMER_NORESTART;
}

/* calibrate port write time */
--
2.1.4