diff options
author | Warner Losh <imp@FreeBSD.org> | 2006-03-30 04:25:45 +0000 |
---|---|---|
committer | Warner Losh <imp@FreeBSD.org> | 2006-03-30 04:25:45 +0000 |
commit | a49ed2a67303d410e00f1be2d08230b1c61c6d41 (patch) | |
tree | d8dafdbd037d524e9f8733af8f937938cce099e6 /sys/dev/pccbb | |
parent | ee516cac43ecd89e2cda1e41a401d2ef2f172fa5 (diff) | |
download | src-a49ed2a67303d410e00f1be2d08230b1c61c6d41.tar.gz src-a49ed2a67303d410e00f1be2d08230b1c61c6d41.zip |
On some laptops, under very high loads, the socket event register read
in the ISR doesn't read the actual socket event register, but instead
reads garbage (usually 0xffffffff, but other times other things).
This totally violates the PCI spec, but happens rarely enough that a
workaround is in order. This adds one test when we have a real
interrupt to service (which is very rare), and doesn't affect the
usualy 'nothing to see here' case at all.
Problem reported by many, but sam@ gave me this workaround after
diagnosing the problem.
Notes
Notes:
svn path=/head/; revision=157281
Diffstat (limited to 'sys/dev/pccbb')
-rw-r--r-- | sys/dev/pccbb/pccbb.c | 15 |
1 files changed, 13 insertions, 2 deletions
diff --git a/sys/dev/pccbb/pccbb.c b/sys/dev/pccbb/pccbb.c index 9d51a790d3ce..f3ced97d31d4 100644 --- a/sys/dev/pccbb/pccbb.c +++ b/sys/dev/pccbb/pccbb.c @@ -650,8 +650,19 @@ cbb_intr(void *arg) struct cbb_softc *sc = arg; uint32_t sockevent; + /* + * Read the socket event. Sometimes, the theory goes, the PCI + * bus is so loaded that it cannot satisfy the read request, so + * we get garbage back from the following read. We have to filter + * out the garbage so that we don't spontaneously reset the card + * under high load. PCI isn't supposed to act like this. No doubt + * this is a bug in the PCI bridge chipset (or cbb brige) that's being + * used in certain amd64 laptops today. Work around the issue by + * assuming that any bits we don't know about being set means that + * we got garbage. + */ sockevent = cbb_get(sc, CBB_SOCKET_EVENT); - if (sockevent != 0) { + if (sockevent != 0 && (sockevent & CBB_SOCKET_EVENT_VALID_MASK) == 0) { /* ack the interrupt */ cbb_set(sc, CBB_SOCKET_EVENT, sockevent); @@ -697,7 +708,7 @@ cbb_intr(void *arg) * indication above. * * We have to call this unconditionally because some bridges deliver - * the even independent of the CBB_SOCKET_EVENT_CD above. + * the event independent of the CBB_SOCKET_EVENT_CD above. */ exca_getb(&sc->exca[0], EXCA_CSC); } |