summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorgallatin <gallatin@FreeBSD.org>2000-07-19 14:33:52 +0000
committergallatin <gallatin@FreeBSD.org>2000-07-19 14:33:52 +0000
commitb0e74737e90dd9a2b77a103978f5e4bfa63edf17 (patch)
tree020cc7532b087697dfe2e6d328cf556bdeb51d08 /sys
parent9f3624548b71b3532a679d6cedf8b009eae2f3f4 (diff)
downloadFreeBSD-src-b0e74737e90dd9a2b77a103978f5e4bfa63edf17.zip
FreeBSD-src-b0e74737e90dd9a2b77a103978f5e4bfa63edf17.tar.gz
Fix an alpha-only race which causes the transmit side of the chip to
lock up under moderate to heavy load. The status & command fields share a 32-bit longword. The programming API of the eepro apparently requires that you update the command field of a transmit slot that you've already given to the card. This means the card could be updating the status field of the same longword at the same time. Since alphas can only operate on 32-bit chunks of memory, both the status & command fields are loaded from memory & operated on in registers when the following line of C is executed: sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S; The race is caused by the card DMA'ing up the status at just the wrong time -- after it has been loaded into a register & before it has been written back. The old value of the status is written back, clobbering the status the card just DMA'ed up. The fact that the card has sent this frame is missed & the transmit engine appears to hang. Luckily, as numerous people on the freebsd-alpha list pointed out, the load-locked/store-conditional instructions used by the atomic functions work with respect changes in memory due to I/O devices. We now use them to safely update the command field. Tested by: Bernd Walter <ticso@mail.cicely.de>
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/fxp/if_fxp.c12
-rw-r--r--sys/pci/if_fxp.c12
2 files changed, 24 insertions, 0 deletions
diff --git a/sys/dev/fxp/if_fxp.c b/sys/dev/fxp/if_fxp.c
index 408ec5d..e86abad 100644
--- a/sys/dev/fxp/if_fxp.c
+++ b/sys/dev/fxp/if_fxp.c
@@ -1053,7 +1053,19 @@ tbdinit:
/*
* Advance the end of list forward.
*/
+
+#ifdef __alpha__
+ /*
+ * On platforms which can't access memory in 16-bit
+ * granularities, we must prevent the card from DMA'ing
+ * up the status while we update the command field.
+ * This could cause us to overwrite the completion status.
+ */
+ atomic_clear_short(&sc->cbl_last->cb_command,
+ FXP_CB_COMMAND_S);
+#else
sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S;
+#endif /*__alpha__*/
sc->cbl_last = txp;
/*
diff --git a/sys/pci/if_fxp.c b/sys/pci/if_fxp.c
index 408ec5d..e86abad 100644
--- a/sys/pci/if_fxp.c
+++ b/sys/pci/if_fxp.c
@@ -1053,7 +1053,19 @@ tbdinit:
/*
* Advance the end of list forward.
*/
+
+#ifdef __alpha__
+ /*
+ * On platforms which can't access memory in 16-bit
+ * granularities, we must prevent the card from DMA'ing
+ * up the status while we update the command field.
+ * This could cause us to overwrite the completion status.
+ */
+ atomic_clear_short(&sc->cbl_last->cb_command,
+ FXP_CB_COMMAND_S);
+#else
sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S;
+#endif /*__alpha__*/
sc->cbl_last = txp;
/*
OpenPOWER on IntegriCloud