summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2008-07-17 16:45:40 +0200
committerMichael Brown2008-07-17 16:45:40 +0200
commit4e033c774b3731488cc7750f2d4d89c12661a254 (patch)
tree6bb41e3a7b7370c948314e7d0a2a4dd7b72a7968
parent[int13] Pairwise swap drive numbers, instead of shifting all drive numbers (diff)
downloadipxe-4e033c774b3731488cc7750f2d4d89c12661a254.tar.gz
ipxe-4e033c774b3731488cc7750f2d4d89c12661a254.tar.xz
ipxe-4e033c774b3731488cc7750f2d4d89c12661a254.zip
[phantom] Guard against partially-written status descriptors
Conjecture: The hardware issues 64-bit DMA writes of status descriptors, which some PCI bridges seem to split into two 32-bit writes in reverse order (i.e. dword 1 first). This means that we sometimes observe a partial status descriptor. Add an explicit check to ensure that the descriptor is complete before processing it. Also ensure that the RDS consumer counter is incremented only when we know that we have actually consumed an RX descriptor.
-rw-r--r--src/drivers/net/phantom/phantom.c43
1 files changed, 28 insertions, 15 deletions
diff --git a/src/drivers/net/phantom/phantom.c b/src/drivers/net/phantom/phantom.c
index 93675e3f..93fc0790 100644
--- a/src/drivers/net/phantom/phantom.c
+++ b/src/drivers/net/phantom/phantom.c
@@ -1445,9 +1445,20 @@ static void phantom_poll ( struct net_device *netdev ) {
/* Check received opcode */
sds_opcode = NX_GET ( sds, opcode );
- switch ( sds_opcode ) {
- case UNM_RXPKT_DESC:
- case UNM_SYN_OFFLOAD:
+ if ( ( sds_opcode == UNM_RXPKT_DESC ) ||
+ ( sds_opcode == UNM_SYN_OFFLOAD ) ) {
+
+ /* Sanity check: ensure that all of the SDS
+ * descriptor has been written.
+ */
+ if ( NX_GET ( sds, total_length ) == 0 ) {
+ DBGC ( phantom, "Phantom %p port %d SDS %d "
+ "incomplete; deferring\n", phantom,
+ phantom_port->port, sds_consumer_idx );
+ /* Leave for next poll() */
+ break;
+ }
+
/* Process received packet */
sds_handle = NX_GET ( sds, handle );
iobuf = phantom_port->rds_iobuf[sds_handle];
@@ -1459,25 +1470,27 @@ static void phantom_poll ( struct net_device *netdev ) {
phantom, phantom_port->port, sds_handle );
netdev_rx ( netdev, iobuf );
phantom_port->rds_iobuf[sds_handle] = NULL;
- break;
- default:
+
+ /* Update RDS consumer counter. This is a
+ * lower bound for the number of descriptors
+ * that have been read by the hardware, since
+ * the hardware must have read at least one
+ * descriptor for each completion that we
+ * receive.
+ */
+ rds_consumer_idx =
+ ( ( rds_consumer_idx + 1 ) % PHN_NUM_RDS );
+ phantom_port->rds_consumer_idx = rds_consumer_idx;
+
+ } else {
+
DBGC ( phantom, "Phantom %p port %d unexpected SDS "
"opcode %02x\n",
phantom, phantom_port->port, sds_opcode );
DBGC_HDA ( phantom, virt_to_bus ( sds ),
sds, sizeof ( *sds ) );
- break;
}
- /* Update RDS consumer counter. This is a lower bound
- * for the number of descriptors that have been read
- * by the hardware, since the hardware must have read
- * at least one descriptor for each completion that we
- * receive.
- */
- rds_consumer_idx = ( ( rds_consumer_idx + 1 ) % PHN_NUM_RDS );
- phantom_port->rds_consumer_idx = rds_consumer_idx;
-
/* Clear status descriptor */
memset ( sds, 0, sizeof ( *sds ) );