Browse Source

[PATCH] prism54: fix potential race in reset scheduling

NET: prism54 - fix potential race in reset scheduling

There appears to be a race in reset scheduling logic - thread
responsible for reseting the interface should clear "reset
pending" flag before restarting the queue, otherwise timeout
handler might not schedule another reset even if it is needed.

This race is mostly theoretical as far as I can see but a race
nonetheless.

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Dmitry Torokhov 18 năm trước cách đây
mục cha
commit
d18e0c4a54

+ 13 - 10
drivers/net/wireless/prism54/islpci_eth.c

@@ -253,6 +253,7 @@ islpci_monitor_rx(islpci_private *priv, struct sk_buff **skb)
 	 * header and without the FCS. But there a is a bit that
 	 * header and without the FCS. But there a is a bit that
 	 * indicates if the packet is corrupted :-) */
 	 * indicates if the packet is corrupted :-) */
 	struct rfmon_header *hdr = (struct rfmon_header *) (*skb)->data;
 	struct rfmon_header *hdr = (struct rfmon_header *) (*skb)->data;
+
 	if (hdr->flags & 0x01)
 	if (hdr->flags & 0x01)
 		/* This one is bad. Drop it ! */
 		/* This one is bad. Drop it ! */
 		return -1;
 		return -1;
@@ -464,10 +465,8 @@ islpci_eth_receive(islpci_private *priv)
 			break;
 			break;
 		}
 		}
 		/* update the fragment address */
 		/* update the fragment address */
-		control_block->rx_data_low[index].address = cpu_to_le32((u32)
-									priv->
-									pci_map_rx_address
-									[index]);
+		control_block->rx_data_low[index].address =
+			cpu_to_le32((u32)priv->pci_map_rx_address[index]);
 		wmb();
 		wmb();
 
 
 		/* increment the driver read pointer */
 		/* increment the driver read pointer */
@@ -484,10 +483,12 @@ islpci_eth_receive(islpci_private *priv)
 void
 void
 islpci_do_reset_and_wake(void *data)
 islpci_do_reset_and_wake(void *data)
 {
 {
-	islpci_private *priv = (islpci_private *) data;
+	islpci_private *priv = data;
+
 	islpci_reset(priv, 1);
 	islpci_reset(priv, 1);
-	netif_wake_queue(priv->ndev);
 	priv->reset_task_pending = 0;
 	priv->reset_task_pending = 0;
+	smp_wmb();
+	netif_wake_queue(priv->ndev);
 }
 }
 
 
 void
 void
@@ -499,12 +500,14 @@ islpci_eth_tx_timeout(struct net_device *ndev)
 	/* increment the transmit error counter */
 	/* increment the transmit error counter */
 	statistics->tx_errors++;
 	statistics->tx_errors++;
 
 
-	printk(KERN_WARNING "%s: tx_timeout", ndev->name);
 	if (!priv->reset_task_pending) {
 	if (!priv->reset_task_pending) {
-		priv->reset_task_pending = 1;
-		printk(", scheduling a reset");
+		printk(KERN_WARNING
+			"%s: tx_timeout, scheduling reset", ndev->name);
 		netif_stop_queue(ndev);
 		netif_stop_queue(ndev);
+		priv->reset_task_pending = 1;
 		schedule_work(&priv->reset_task);
 		schedule_work(&priv->reset_task);
+	} else {
+		printk(KERN_WARNING
+			"%s: tx_timeout, waiting for reset", ndev->name);
 	}
 	}
-	printk("\n");
 }
 }