[PATCH] sky2: handle out of memory on admin changes

Don't die if we run out of memory on mtu or ring parameter change.
For other admin operations, don't rebuild Rx ring, just restart the PHY.

Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
index 7af72cd..1d183d5 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -481,6 +481,14 @@
 		gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK);
 }
 
+/* Force a renegotiation */
+static void sky2_phy_reinit(struct sky2_port *sky2)
+{
+	down(&sky2->phy_sema);
+	sky2_phy_init(sky2->hw, sky2->port);
+	up(&sky2->phy_sema);
+}
+
 static void sky2_mac_init(struct sky2_hw *hw, unsigned port)
 {
 	struct sky2_port *sky2 = netdev_priv(hw->dev[port]);
@@ -1014,18 +1022,22 @@
 	return 0;
 
 err_out:
-	if (sky2->rx_le)
+	if (sky2->rx_le) {
 		pci_free_consistent(hw->pdev, RX_LE_BYTES,
 				    sky2->rx_le, sky2->rx_le_map);
-	if (sky2->tx_le)
+		sky2->rx_le = NULL;
+	}
+	if (sky2->tx_le) {
 		pci_free_consistent(hw->pdev,
 				    TX_RING_SIZE * sizeof(struct sky2_tx_le),
 				    sky2->tx_le, sky2->tx_le_map);
-	if (sky2->tx_ring)
-		kfree(sky2->tx_ring);
-	if (sky2->rx_ring)
-		kfree(sky2->rx_ring);
+		sky2->tx_le = NULL;
+	}
+	kfree(sky2->tx_ring);
+	kfree(sky2->rx_ring);
 
+	sky2->tx_ring = NULL;
+	sky2->rx_ring = NULL;
 	return err;
 }
 
@@ -1291,6 +1303,10 @@
 	unsigned port = sky2->port;
 	u16 ctrl;
 
+	/* Never really got started! */
+	if (!sky2->tx_le)
+		return 0;
+
 	if (netif_msg_ifdown(sky2))
 		printk(KERN_INFO PFX "%s: disabling interface\n", dev->name);
 
@@ -1365,6 +1381,12 @@
 			    sky2->tx_le, sky2->tx_le_map);
 	kfree(sky2->tx_ring);
 
+	sky2->tx_le = NULL;
+	sky2->rx_le = NULL;
+
+	sky2->rx_ring = NULL;
+	sky2->tx_ring = NULL;
+
 	return 0;
 }
 
@@ -1636,12 +1658,17 @@
 	sky2_write8(hw, RB_ADDR(rxqaddr[sky2->port], RB_CTRL), RB_ENA_OP_MD);
 
 	err = sky2_rx_start(sky2);
-	gma_write16(hw, sky2->port, GM_GP_CTRL, ctl);
-
-	netif_poll_disable(hw->dev[0]);
-	netif_wake_queue(dev);
 	sky2_write32(hw, B0_IMSK, hw->intr_mask);
 
+	if (err)
+		dev_close(dev);
+	else {
+		gma_write16(hw, sky2->port, GM_GP_CTRL, ctl);
+
+		netif_poll_enable(hw->dev[0]);
+		netif_wake_queue(dev);
+	}
+
 	return err;
 }
 
@@ -2315,10 +2342,8 @@
 	sky2->autoneg = ecmd->autoneg;
 	sky2->advertising = ecmd->advertising;
 
-	if (netif_running(dev)) {
-		sky2_down(dev);
-		sky2_up(dev);
-	}
+	if (netif_running(dev))
+		sky2_phy_reinit(sky2);
 
 	return 0;
 }
@@ -2389,17 +2414,11 @@
 static int sky2_nway_reset(struct net_device *dev)
 {
 	struct sky2_port *sky2 = netdev_priv(dev);
-	struct sky2_hw *hw = sky2->hw;
 
 	if (sky2->autoneg != AUTONEG_ENABLE)
 		return -EINVAL;
 
-	netif_stop_queue(dev);
-
-	down(&sky2->phy_sema);
-	sky2_phy_reset(hw, sky2->port);
-	sky2_phy_init(hw, sky2->port);
-	up(&sky2->phy_sema);
+	sky2_phy_reinit(sky2);
 
 	return 0;
 }
@@ -2477,20 +2496,20 @@
 {
 	struct sky2_port *sky2 = netdev_priv(dev);
 	struct sockaddr *addr = p;
-	int err = 0;
 
 	if (!is_valid_ether_addr(addr->sa_data))
 		return -EADDRNOTAVAIL;
 
-	sky2_down(dev);
 	memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
 	memcpy_toio(sky2->hw->regs + B2_MAC_1 + sky2->port * 8,
 		    dev->dev_addr, ETH_ALEN);
 	memcpy_toio(sky2->hw->regs + B2_MAC_2 + sky2->port * 8,
 		    dev->dev_addr, ETH_ALEN);
-	if (dev->flags & IFF_UP)
-		err = sky2_up(dev);
-	return err;
+
+	if (netif_running(dev))
+		sky2_phy_reinit(sky2);
+
+	return 0;
 }
 
 static void sky2_set_multicast(struct net_device *dev)
@@ -2648,10 +2667,7 @@
 	sky2->tx_pause = ecmd->tx_pause != 0;
 	sky2->rx_pause = ecmd->rx_pause != 0;
 
-	if (netif_running(dev)) {
-		sky2_down(dev);
-		err = sky2_up(dev);
-	}
+	sky2_phy_reinit(sky2);
 
 	return err;
 }
@@ -2813,8 +2829,11 @@
 	sky2->rx_pending = ering->rx_pending;
 	sky2->tx_pending = ering->tx_pending;
 
-	if (netif_running(dev))
+	if (netif_running(dev)) {
 		err = sky2_up(dev);
+		if (err)
+			dev_close(dev);
+	}
 
 	return err;
 }
@@ -3195,7 +3214,8 @@
 		if (dev) {
 			if (netif_running(dev)) {
 				netif_device_attach(dev);
-				sky2_up(dev);
+				if (sky2_up(dev))
+					dev_close(dev);
 			}
 		}
 	}