[PATCH] IB/ipath: fix lost interrupts on HT-400
Do an extra check to see if in-memory tail changed while processing packets, and if so, going back through the loop again (but only once per call to ipath_kreceive()). In practice, this seems to be enough to guarantee that if we crossed the clearing of an interrupt at start of ipath_intr with a scheduled tail register update, that we'll process the "extra" packet that lost the interrupt because we cleared it just as it was about to arrive. Signed-off-by: Dave Olson <dave.olson@qlogic.com> Signed-off-by: Bryan O'Sullivan <bryan.osullivan@qlogic.com> Cc: "Michael S. Tsirkin" <mst@mellanox.co.il> Cc: Roland Dreier <rolandd@cisco.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
f5f99929ac
commit
57abad25f8
|
@ -870,7 +870,7 @@ void ipath_kreceive(struct ipath_devdata *dd)
|
||||||
const u32 maxcnt = dd->ipath_rcvhdrcnt * rsize; /* words */
|
const u32 maxcnt = dd->ipath_rcvhdrcnt * rsize; /* words */
|
||||||
u32 etail = -1, l, hdrqtail;
|
u32 etail = -1, l, hdrqtail;
|
||||||
struct ips_message_header *hdr;
|
struct ips_message_header *hdr;
|
||||||
u32 eflags, i, etype, tlen, pkttot = 0, updegr=0;
|
u32 eflags, i, etype, tlen, pkttot = 0, updegr=0, reloop=0;
|
||||||
static u64 totcalls; /* stats, may eventually remove */
|
static u64 totcalls; /* stats, may eventually remove */
|
||||||
char emsg[128];
|
char emsg[128];
|
||||||
|
|
||||||
|
@ -885,9 +885,11 @@ void ipath_kreceive(struct ipath_devdata *dd)
|
||||||
goto bail;
|
goto bail;
|
||||||
|
|
||||||
l = dd->ipath_port0head;
|
l = dd->ipath_port0head;
|
||||||
if (l == (u32)le64_to_cpu(*dd->ipath_hdrqtailptr))
|
hdrqtail = (u32) le64_to_cpu(*dd->ipath_hdrqtailptr);
|
||||||
|
if (l == hdrqtail)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
reloop:
|
||||||
/* read only once at start for performance */
|
/* read only once at start for performance */
|
||||||
hdrqtail = (u32)le64_to_cpu(*dd->ipath_hdrqtailptr);
|
hdrqtail = (u32)le64_to_cpu(*dd->ipath_hdrqtailptr);
|
||||||
|
|
||||||
|
@ -1011,7 +1013,7 @@ void ipath_kreceive(struct ipath_devdata *dd)
|
||||||
*/
|
*/
|
||||||
if (l == hdrqtail || (i && !(i&0xf))) {
|
if (l == hdrqtail || (i && !(i&0xf))) {
|
||||||
u64 lval;
|
u64 lval;
|
||||||
if (l == hdrqtail) /* want interrupt only on last */
|
if (l == hdrqtail) /* PE-800 interrupt only on last */
|
||||||
lval = dd->ipath_rhdrhead_intr_off | l;
|
lval = dd->ipath_rhdrhead_intr_off | l;
|
||||||
else
|
else
|
||||||
lval = l;
|
lval = l;
|
||||||
|
@ -1024,6 +1026,23 @@ void ipath_kreceive(struct ipath_devdata *dd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!dd->ipath_rhdrhead_intr_off && !reloop) {
|
||||||
|
/* HT-400 workaround; we can have a race clearing chip
|
||||||
|
* interrupt with another interrupt about to be delivered,
|
||||||
|
* and can clear it before it is delivered on the GPIO
|
||||||
|
* workaround. By doing the extra check here for the
|
||||||
|
* in-memory tail register updating while we were doing
|
||||||
|
* earlier packets, we "almost" guarantee we have covered
|
||||||
|
* that case.
|
||||||
|
*/
|
||||||
|
u32 hqtail = (u32)le64_to_cpu(*dd->ipath_hdrqtailptr);
|
||||||
|
if (hqtail != hdrqtail) {
|
||||||
|
hdrqtail = hqtail;
|
||||||
|
reloop = 1; /* loop 1 extra time at most */
|
||||||
|
goto reloop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pkttot += i;
|
pkttot += i;
|
||||||
|
|
||||||
dd->ipath_port0head = l;
|
dd->ipath_port0head = l;
|
||||||
|
|
|
@ -766,7 +766,7 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
|
||||||
u32 istat, chk0rcv = 0;
|
u32 istat, chk0rcv = 0;
|
||||||
ipath_err_t estat = 0;
|
ipath_err_t estat = 0;
|
||||||
irqreturn_t ret;
|
irqreturn_t ret;
|
||||||
u32 p0bits, oldhead;
|
u32 oldhead, curtail;
|
||||||
static unsigned unexpected = 0;
|
static unsigned unexpected = 0;
|
||||||
static const u32 port0rbits = (1U<<INFINIPATH_I_RCVAVAIL_SHIFT) |
|
static const u32 port0rbits = (1U<<INFINIPATH_I_RCVAVAIL_SHIFT) |
|
||||||
(1U<<INFINIPATH_I_RCVURG_SHIFT);
|
(1U<<INFINIPATH_I_RCVURG_SHIFT);
|
||||||
|
@ -809,15 +809,16 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
|
||||||
* lose intr for later packets that arrive while we are processing.
|
* lose intr for later packets that arrive while we are processing.
|
||||||
*/
|
*/
|
||||||
oldhead = dd->ipath_port0head;
|
oldhead = dd->ipath_port0head;
|
||||||
if (oldhead != (u32) le64_to_cpu(*dd->ipath_hdrqtailptr)) {
|
curtail = (u32)le64_to_cpu(*dd->ipath_hdrqtailptr);
|
||||||
|
if (oldhead != curtail) {
|
||||||
if (dd->ipath_flags & IPATH_GPIO_INTR) {
|
if (dd->ipath_flags & IPATH_GPIO_INTR) {
|
||||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
|
ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
|
||||||
(u64) (1 << 2));
|
(u64) (1 << 2));
|
||||||
p0bits = port0rbits | INFINIPATH_I_GPIO;
|
istat = port0rbits | INFINIPATH_I_GPIO;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
p0bits = port0rbits;
|
istat = port0rbits;
|
||||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, p0bits);
|
ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, istat);
|
||||||
ipath_kreceive(dd);
|
ipath_kreceive(dd);
|
||||||
if (oldhead != dd->ipath_port0head) {
|
if (oldhead != dd->ipath_port0head) {
|
||||||
ipath_stats.sps_fastrcvint++;
|
ipath_stats.sps_fastrcvint++;
|
||||||
|
@ -827,7 +828,6 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
|
||||||
}
|
}
|
||||||
|
|
||||||
istat = ipath_read_kreg32(dd, dd->ipath_kregs->kr_intstatus);
|
istat = ipath_read_kreg32(dd, dd->ipath_kregs->kr_intstatus);
|
||||||
p0bits = port0rbits;
|
|
||||||
|
|
||||||
if (unlikely(!istat)) {
|
if (unlikely(!istat)) {
|
||||||
ipath_stats.sps_nullintr++;
|
ipath_stats.sps_nullintr++;
|
||||||
|
@ -890,19 +890,19 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
|
||||||
else {
|
else {
|
||||||
/* Clear GPIO status bit 2 */
|
/* Clear GPIO status bit 2 */
|
||||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
|
ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
|
||||||
(u64) (1 << 2));
|
(u64) (1 << 2));
|
||||||
p0bits |= INFINIPATH_I_GPIO;
|
|
||||||
chk0rcv = 1;
|
chk0rcv = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chk0rcv |= istat & p0bits;
|
chk0rcv |= istat & port0rbits;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* clear the ones we will deal with on this round
|
* Clear the interrupt bits we found set, unless they are receive
|
||||||
* We clear it early, mostly for receive interrupts, so we
|
* related, in which case we already cleared them above, and don't
|
||||||
* know the chip will have seen this by the time we process
|
* want to clear them again, because we might lose an interrupt.
|
||||||
* the queue, and will re-interrupt if necessary. The processor
|
* Clear it early, so we "know" know the chip will have seen this by
|
||||||
* itself won't take the interrupt again until we return.
|
* the time we process the queue, and will re-interrupt if necessary.
|
||||||
|
* The processor itself won't take the interrupt again until we return.
|
||||||
*/
|
*/
|
||||||
ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, istat);
|
ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, istat);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue