Commit dcafaf66 authored by Alessandro Rubini's avatar Alessandro Rubini

kernel/wr-nic (and doc): disable interrupts if too many

When using the SPEC as White Rabbit grandmaster, you need to feed it
with 10MHz, which would kill the computer with an interrupt flood.

DIO interrupts are now disabled if they consume more than 80% of the
total time, which usually triggers between 100kHz and 200kHz. Network
interrupts are not affected.

Thanks to Anders Wallin for reporting the problem and helping in the
diagnosis.
Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent 8b7a486f
......@@ -33,13 +33,13 @@
@setchapternewpage off
@set update-month December 2012
@set update-month April 2013
@finalout
@titlepage
@title SPEC Software Support
@subtitle Version 2.2 (@value{update-month})
@c @subtitle Version 2.2 (@value{update-month})
@subtitle A driver for the SPEC card and its FMC modules
@author Alessandro Rubini for CERN (BE-CO-HT)
@end titlepage
......@@ -977,6 +977,39 @@ Example uses of the tool follow:
wr-dio-cmd wr0 mode Ii--0
@end example
@c ==========================================================================
@node Timestamping Fast Input Signals
@section Timestamping Fast Input Signals
When timestamping pulses in the @i{simple-DIO} mezzanine board, an
interrupt is generated to notify the driver that a new timestamp is
pending. On recent computers this works reliably up to more than
100kHz, but clearly there is a point where the system locks up,
because it spends all of its time in interrupt handling.
This problem is transient: as soon as you remove the offending cable
the system recovers. However, you need a 10MHz input signal if you
want to run your SPEC device to be a White Rabbit @i{grandmaster}. In
order to support that, the driver disables DIO interrupts when the
time spent in interrupt management exceeds 80% of the total time,
averaged over one thousand interrupt events. Ethernet interrupts
are not affected. The fact is reported by a kernel message, using
the PCI address of the card that triggered the problem.
@example
spec 0000:04:00.0: DIO irq takes > 80% CPU time: disabling
@end example
This choice allows stamping your pulse trains up to a few dozen
kilohertz and still be able to feed higher frequencies without manual
intervention. However, after DIO interrupts are disabled, the only
way to re-enable them is removing and reloading the device driver.
@b{Note}: if you run two SPEC cards, and one is fed with high frequency
pulses, it may happen that interrupts are disabled on both boards.
The safeguard is currently not very refined, as it was implemented in
a hurry.
@c ==========================================================================
@node WR-DIO Pulse per Second
@section WR-DIO Pulse per Second
......
......@@ -20,6 +20,7 @@
#include "spec-nic.h"
#include "wr_nic/wr-nic.h"
#include "wr-dio.h"
#include "wbgen-regs/vic-regs.h"
#ifdef DIO_STAT
#define wrn_stat 1
......@@ -411,9 +412,12 @@ irqreturn_t wrn_dio_interrupt(struct fmc_device *fmc)
{
struct platform_device *pdev = fmc->mezzanine_data;
struct wrn_drvdata *drvdata = pdev->dev.platform_data;
struct VIC_WB __iomem *vic = drvdata->vic_base;
struct DIO_WB __iomem *dio = drvdata->wrdio_base;
void __iomem *base = drvdata->wrdio_base;
struct dio_device *d = drvdata->mezzanine_data;
static ktime_t t_ini, t_end;
static int rate_avg;
struct dio_channel *c;
struct timespec *ts;
struct regmap *map;
......@@ -427,6 +431,29 @@ irqreturn_t wrn_dio_interrupt(struct fmc_device *fmc)
return IRQ_NONE;
}
/* Protect against interrupts taking 100% of cpu time */
if (ktime_to_ns(t_end)) {
int rate;
u64 offtime, ontime;
ontime = ktime_to_ns(t_end) - ktime_to_ns(t_ini);
t_ini = ktime_get();
offtime = ktime_to_ns(t_ini) - ktime_to_ns(t_end);
/* avoid __udivdi3 */
if (offtime > 100 * ontime)
rate = 0;
else
rate = ((int)ontime * 100) / (int)offtime;
rate_avg = (rate_avg * 1023 + rate) / 1024;
if (rate_avg > 80) {
dev_warn(fmc->hwdev, "DIO irq takes > 80%% CPU time: "
"disabling\n");
writel(WRN_VIC_MASK_DIO, &vic->IDR);
}
}
mask = readl(&dio->EIC_ISR) & WRN_DIO_IRQ_MASK;
/* Three indexes: channel, channel-mask, channel pointer */
......@@ -466,6 +493,7 @@ irqreturn_t wrn_dio_interrupt(struct fmc_device *fmc)
}
wake_up_interruptible(&c->q);
}
t_end = ktime_get();
return IRQ_HANDLED;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment