From: Chris Wright Date: Thu, 5 Jan 2006 02:45:16 +0000 (-0800) Subject: Add skge fix from shemminger X-Git-Tag: v2.6.14.6~10 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a2df3c6c6f15d2bd105b95b1797717f7e5580701;p=thirdparty%2Fkernel%2Fstable-queue.git Add skge fix from shemminger --- diff --git a/queue/series b/queue/series index c549f093369..29f1414a949 100644 --- a/queue/series +++ b/queue/series @@ -1,3 +1,4 @@ bridge-fix-faulty-check-in-br_stp_recalculate_bridge_id.patch ufs-inode-i_sem-is-not-released-in-error-path.patch acpi-fix-asus_acpi-on-samsung-p30-p35.patch +skge-handle-out-of-memory-on-ring-changes.patch diff --git a/queue/skge-handle-out-of-memory-on-ring-changes.patch b/queue/skge-handle-out-of-memory-on-ring-changes.patch new file mode 100644 index 00000000000..da80d24c4df --- /dev/null +++ b/queue/skge-handle-out-of-memory-on-ring-changes.patch @@ -0,0 +1,224 @@ +From stable-bounces@linux.kernel.org Wed Jan 4 15:55:59 2006 +Date: Wed, 4 Jan 2006 15:52:28 -0800 +From: Stephen Hemminger +To: stable@kernel.org +Message-ID: <20060104155228.014d6326@dxpl.pdx.osdl.net> +Subject: [PATCH] skge: handle out of memory on ring changes + +Please consider this for 2.6.15.1; it fixes several cases where +the skge driver can get in a bad state and later crash; if an +admin operation that causes a restart fails from out of memory. +Such as changing the MTU or increasing the ring size. + +The fixes involve checking the return value and doing necessary +unwinds. Or in some cases avoiding doing a full restart. + +The same code is the netdev-2.6 tree for 2.6.16 but as separate pieces + +Signed-off-by: Stephen Hemminger +Signed-off-by: Chris Wright +--- + + + drivers/net/skge.c | 80 +++++++++++++++++++++++++++++++---------------------- + 1 files changed, 48 insertions(+), 32 deletions(-) + +Index: linux-2.6.15.y/drivers/net/skge.c +=================================================================== +--- linux-2.6.15.y.orig/drivers/net/skge.c ++++ linux-2.6.15.y/drivers/net/skge.c +@@ -43,7 +43,7 @@ + #include "skge.h" + + #define DRV_NAME "skge" +-#define DRV_VERSION "1.2" ++#define DRV_VERSION "1.3" + #define PFX DRV_NAME " " + + #define DEFAULT_TX_RING_SIZE 128 +@@ -88,15 +88,14 @@ MODULE_DEVICE_TABLE(pci, skge_id_table); + + static int skge_up(struct net_device *dev); + static int skge_down(struct net_device *dev); ++static void skge_phy_reset(struct skge_port *skge); + static void skge_tx_clean(struct skge_port *skge); + static int xm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val); + static int gm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val); + static void genesis_get_stats(struct skge_port *skge, u64 *data); + static void yukon_get_stats(struct skge_port *skge, u64 *data); + static void yukon_init(struct skge_hw *hw, int port); +-static void yukon_reset(struct skge_hw *hw, int port); + static void genesis_mac_init(struct skge_hw *hw, int port); +-static void genesis_reset(struct skge_hw *hw, int port); + static void genesis_link_up(struct skge_port *skge); + + /* Avoid conditionals by using array */ +@@ -276,10 +275,9 @@ static int skge_set_settings(struct net_ + skge->autoneg = ecmd->autoneg; + skge->advertising = ecmd->advertising; + +- if (netif_running(dev)) { +- skge_down(dev); +- skge_up(dev); +- } ++ if (netif_running(dev)) ++ skge_phy_reset(skge); ++ + return (0); + } + +@@ -399,6 +397,7 @@ static int skge_set_ring_param(struct ne + struct ethtool_ringparam *p) + { + struct skge_port *skge = netdev_priv(dev); ++ int err; + + if (p->rx_pending == 0 || p->rx_pending > MAX_RX_RING_SIZE || + p->tx_pending == 0 || p->tx_pending > MAX_TX_RING_SIZE) +@@ -409,7 +408,11 @@ static int skge_set_ring_param(struct ne + + if (netif_running(dev)) { + skge_down(dev); +- skge_up(dev); ++ err = skge_up(dev); ++ if (err) ++ dev_close(dev); ++ else ++ dev->set_multicast_list(dev); + } + + return 0; +@@ -430,21 +433,11 @@ static void skge_set_msglevel(struct net + static int skge_nway_reset(struct net_device *dev) + { + struct skge_port *skge = netdev_priv(dev); +- struct skge_hw *hw = skge->hw; +- int port = skge->port; + + if (skge->autoneg != AUTONEG_ENABLE || !netif_running(dev)) + return -EINVAL; + +- spin_lock_bh(&hw->phy_lock); +- if (hw->chip_id == CHIP_ID_GENESIS) { +- genesis_reset(hw, port); +- genesis_mac_init(hw, port); +- } else { +- yukon_reset(hw, port); +- yukon_init(hw, port); +- } +- spin_unlock_bh(&hw->phy_lock); ++ skge_phy_reset(skge); + return 0; + } + +@@ -516,10 +509,8 @@ static int skge_set_pauseparam(struct ne + else + skge->flow_control = FLOW_MODE_NONE; + +- if (netif_running(dev)) { +- skge_down(dev); +- skge_up(dev); +- } ++ if (netif_running(dev)) ++ skge_phy_reset(skge); + return 0; + } + +@@ -1935,7 +1926,6 @@ static void yukon_link_down(struct skge_ + + } + +- yukon_reset(hw, port); + skge_link_down(skge); + + yukon_init(hw, port); +@@ -2019,6 +2009,22 @@ static void yukon_phy_intr(struct skge_p + /* XXX restart autonegotiation? */ + } + ++static void skge_phy_reset(struct skge_port *skge) ++{ ++ struct skge_hw *hw = skge->hw; ++ int port = skge->port; ++ ++ netif_stop_queue(skge->netdev); ++ netif_carrier_off(skge->netdev); ++ ++ spin_lock_bh(&hw->phy_lock); ++ if (hw->chip_id == CHIP_ID_GENESIS) ++ genesis_mac_init(hw, port); ++ else ++ yukon_init(hw, port); ++ spin_unlock_bh(&hw->phy_lock); ++} ++ + /* Basic MII support */ + static int skge_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) + { +@@ -2187,6 +2193,7 @@ static int skge_up(struct net_device *de + kfree(skge->rx_ring.start); + free_pci_mem: + pci_free_consistent(hw->pdev, skge->mem_size, skge->mem, skge->dma); ++ skge->mem = NULL; + + return err; + } +@@ -2197,6 +2204,9 @@ static int skge_down(struct net_device * + struct skge_hw *hw = skge->hw; + int port = skge->port; + ++ if (skge->mem == NULL) ++ return 0; ++ + if (netif_msg_ifdown(skge)) + printk(KERN_INFO PFX "%s: disabling interface\n", dev->name); + +@@ -2253,6 +2263,7 @@ static int skge_down(struct net_device * + kfree(skge->rx_ring.start); + kfree(skge->tx_ring.start); + pci_free_consistent(hw->pdev, skge->mem_size, skge->mem, skge->dma); ++ skge->mem = NULL; + return 0; + } + +@@ -2413,18 +2424,23 @@ static void skge_tx_timeout(struct net_d + + static int skge_change_mtu(struct net_device *dev, int new_mtu) + { +- int err = 0; +- int running = netif_running(dev); ++ int err; + + if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU) + return -EINVAL; + ++ if (!netif_running(dev)) { ++ dev->mtu = new_mtu; ++ return 0; ++ } ++ ++ skge_down(dev); + +- if (running) +- skge_down(dev); + dev->mtu = new_mtu; +- if (running) +- skge_up(dev); ++ ++ err = skge_up(dev); ++ if (err) ++ dev_close(dev); + + return err; + } +@@ -3398,8 +3414,8 @@ static int skge_resume(struct pci_dev *p + struct net_device *dev = hw->dev[i]; + if (dev) { + netif_device_attach(dev); +- if (netif_running(dev)) +- skge_up(dev); ++ if (netif_running(dev) && skge_up(dev)) ++ dev_close(dev); + } + } + return 0;