Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 57abad25 authored by Bryan O'Sullivan's avatar Bryan O'Sullivan Committed by Linus Torvalds
Browse files

[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: default avatarDave Olson <dave.olson@qlogic.com>
Signed-off-by: default avatarBryan O'Sullivan <bryan.osullivan@qlogic.com>
Cc: "Michael S. Tsirkin" <mst@mellanox.co.il>
Cc: Roland Dreier <rolandd@cisco.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent f5f99929
Loading
Loading
Loading
Loading
+22 −3
Original line number Diff line number Diff line
@@ -870,7 +870,7 @@ void ipath_kreceive(struct ipath_devdata *dd)
	const u32 maxcnt = dd->ipath_rcvhdrcnt * rsize;	/* words */
	u32 etail = -1, l, hdrqtail;
	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 */
	char emsg[128];

@@ -885,9 +885,11 @@ void ipath_kreceive(struct ipath_devdata *dd)
		goto bail;

	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;

reloop:
	/* read only once at start for performance */
	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))) {
			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;
			else
				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;

	dd->ipath_port0head = l;
+14 −14
Original line number Diff line number Diff line
@@ -766,7 +766,7 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
	u32 istat, chk0rcv = 0;
	ipath_err_t estat = 0;
	irqreturn_t ret;
	u32 p0bits, oldhead;
	u32 oldhead, curtail;
	static unsigned unexpected = 0;
	static const u32 port0rbits = (1U<<INFINIPATH_I_RCVAVAIL_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.
	 */
	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) {
			ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
					 (u64) (1 << 2));
			p0bits = port0rbits | INFINIPATH_I_GPIO;
			istat = port0rbits | INFINIPATH_I_GPIO;
		}
		else
			p0bits = port0rbits;
		ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, p0bits);
			istat = port0rbits;
		ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, istat);
		ipath_kreceive(dd);
		if (oldhead != dd->ipath_port0head) {
			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);
	p0bits = port0rbits;

	if (unlikely(!istat)) {
		ipath_stats.sps_nullintr++;
@@ -891,18 +891,18 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
			/* Clear GPIO status bit 2 */
			ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
					(u64) (1 << 2));
			p0bits |= INFINIPATH_I_GPIO;
			chk0rcv = 1;
		}
	}
	chk0rcv |= istat & p0bits;
	chk0rcv |= istat & port0rbits;

	/*
	 * clear the ones we will deal with on this round
	 * We clear it early, mostly for receive interrupts, so we
	 * know the chip will have seen this by the time we process
	 * the queue, and will re-interrupt if necessary.  The processor
	 * itself won't take the interrupt again until we return.
	 * Clear the interrupt bits we found set, unless they are receive
	 * related, in which case we already cleared them above, and don't
	 * want to clear them again, because we might lose an interrupt.
	 * Clear it early, so we "know" know the chip will have seen this by
	 * 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);