diff options
author | Scott Wood <scottwood@freescale.com> | 2007-05-16 16:06:59 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-05-17 20:43:15 -0400 |
commit | 3b6330ce2a3e1f152f79a6203f73d23356e243a7 (patch) | |
tree | 3325c488b05be904ecddd176a1eea3cb3fe2e6a1 /drivers | |
parent | dbf2e8585971f2a8b1f60a188dc245fd2f8f81b3 (diff) |
gianfar: Add I/O barriers when touching buffer descriptor ownership.
The hardware must not see that is given ownership of a buffer until it is
completely written, and when the driver receives ownership of a buffer,
it must ensure that any other reads to the buffer reflect its final
state. Thus, I/O barriers are added where required.
Without this patch, I have observed GCC reordering the setting of
bdp->length and bdp->status in gfar_new_skb. Hardware reordering
was also theoretically possible.
Signed-off-by: Scott Wood <scottwood@freescale.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/gianfar.c | 11 |
1 files changed, 11 insertions, 0 deletions
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index b666a0cc0642..f5b3cba23fc5 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c | |||
@@ -1025,6 +1025,15 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
1025 | 1025 | ||
1026 | dev->trans_start = jiffies; | 1026 | dev->trans_start = jiffies; |
1027 | 1027 | ||
1028 | /* The powerpc-specific eieio() is used, as wmb() has too strong | ||
1029 | * semantics (it requires synchronization between cacheable and | ||
1030 | * uncacheable mappings, which eieio doesn't provide and which we | ||
1031 | * don't need), thus requiring a more expensive sync instruction. At | ||
1032 | * some point, the set of architecture-independent barrier functions | ||
1033 | * should be expanded to include weaker barriers. | ||
1034 | */ | ||
1035 | |||
1036 | eieio(); | ||
1028 | txbdp->status = status; | 1037 | txbdp->status = status; |
1029 | 1038 | ||
1030 | /* If this was the last BD in the ring, the next one */ | 1039 | /* If this was the last BD in the ring, the next one */ |
@@ -1301,6 +1310,7 @@ struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp) | |||
1301 | bdp->length = 0; | 1310 | bdp->length = 0; |
1302 | 1311 | ||
1303 | /* Mark the buffer empty */ | 1312 | /* Mark the buffer empty */ |
1313 | eieio(); | ||
1304 | bdp->status |= (RXBD_EMPTY | RXBD_INTERRUPT); | 1314 | bdp->status |= (RXBD_EMPTY | RXBD_INTERRUPT); |
1305 | 1315 | ||
1306 | return skb; | 1316 | return skb; |
@@ -1484,6 +1494,7 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit) | |||
1484 | bdp = priv->cur_rx; | 1494 | bdp = priv->cur_rx; |
1485 | 1495 | ||
1486 | while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) { | 1496 | while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) { |
1497 | rmb(); | ||
1487 | skb = priv->rx_skbuff[priv->skb_currx]; | 1498 | skb = priv->rx_skbuff[priv->skb_currx]; |
1488 | 1499 | ||
1489 | if (!(bdp->status & | 1500 | if (!(bdp->status & |