uml-fix-kernel-vs-libc-symbols-clash.patch
uml-stop-using-libc-asm-user.h.patch
uml-kill-subprocesses-on-exit.patch
+skge-ram-buffer-fix.patch
+skge-xm-phy-poll.patch
+sky2-status-race.patch
+sky2-ethdump.patch
+sky2-phy-power.patch
--- /dev/null
+From stable-bounces@linux.kernel.org Tue Nov 6 14:18:31 2007
+From: Stephen Hemminger <shemminger@linux-foundation.org>
+Date: Tue, 06 Nov 2007 14:12:29 -0800
+Subject: [stable] [PATCH 1/6] skge: fix ram buffer size calculation
+To: stable@kernel.org
+Message-ID: <20071106221308.286029028@linux-foundation.org>
+Content-Disposition: inline; filename=skge-ram-buffer-fix.patch
+
+From: Stephen Hemminger <shemminger@linux-foundation.org>
+
+patch 7fb7ac241162dc51ec0f7644d4a97b2855213c32 in mainline.
+
+This fixes problems with transmit hangs on older fiber based SysKonnect boards.
+
+Adjust ram buffer sizing calculation to make it correct on all boards
+and make it like the code in sky2 driver.
+
+Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+Signed-off-by: Jeff Garzik <jeff@garzik.org>
+
+---
+ drivers/net/skge.c | 51 ++++++++++++++++++++++++---------------------------
+ 1 file changed, 24 insertions(+), 27 deletions(-)
+
+--- a/drivers/net/skge.c
++++ b/drivers/net/skge.c
+@@ -2400,32 +2400,31 @@ static int skge_ioctl(struct net_device
+ return err;
+ }
+
+-static void skge_ramset(struct skge_hw *hw, u16 q, u32 start, size_t len)
++/* Assign Ram Buffer allocation to queue */
++static void skge_ramset(struct skge_hw *hw, u16 q, u32 start, u32 space)
+ {
+ u32 end;
+
+- start /= 8;
+- len /= 8;
+- end = start + len - 1;
++ /* convert from K bytes to qwords used for hw register */
++ start *= 1024/8;
++ space *= 1024/8;
++ end = start + space - 1;
+
+ skge_write8(hw, RB_ADDR(q, RB_CTRL), RB_RST_CLR);
+ skge_write32(hw, RB_ADDR(q, RB_START), start);
++ skge_write32(hw, RB_ADDR(q, RB_END), end);
+ skge_write32(hw, RB_ADDR(q, RB_WP), start);
+ skge_write32(hw, RB_ADDR(q, RB_RP), start);
+- skge_write32(hw, RB_ADDR(q, RB_END), end);
+
+ if (q == Q_R1 || q == Q_R2) {
++ u32 tp = space - space/4;
++
+ /* Set thresholds on receive queue's */
+- skge_write32(hw, RB_ADDR(q, RB_RX_UTPP),
+- start + (2*len)/3);
+- skge_write32(hw, RB_ADDR(q, RB_RX_LTPP),
+- start + (len/3));
+- } else {
+- /* Enable store & forward on Tx queue's because
+- * Tx FIFO is only 4K on Genesis and 1K on Yukon
+- */
++ skge_write32(hw, RB_ADDR(q, RB_RX_UTPP), tp);
++ skge_write32(hw, RB_ADDR(q, RB_RX_LTPP), space/4);
++ } else if (hw->chip_id != CHIP_ID_GENESIS)
++ /* Genesis Tx Fifo is too small for normal store/forward */
+ skge_write8(hw, RB_ADDR(q, RB_CTRL), RB_ENA_STFWD);
+- }
+
+ skge_write8(hw, RB_ADDR(q, RB_CTRL), RB_ENA_OP_MD);
+ }
+@@ -2453,7 +2452,7 @@ static int skge_up(struct net_device *de
+ struct skge_port *skge = netdev_priv(dev);
+ struct skge_hw *hw = skge->hw;
+ int port = skge->port;
+- u32 chunk, ram_addr;
++ u32 ramaddr, ramsize, rxspace;
+ size_t rx_size, tx_size;
+ int err;
+
+@@ -2508,14 +2507,15 @@ static int skge_up(struct net_device *de
+ spin_unlock_bh(&hw->phy_lock);
+
+ /* Configure RAMbuffers */
+- chunk = hw->ram_size / ((hw->ports + 1)*2);
+- ram_addr = hw->ram_offset + 2 * chunk * port;
++ ramsize = (hw->ram_size - hw->ram_offset) / hw->ports;
++ ramaddr = hw->ram_offset + port * ramsize;
++ rxspace = 8 + (2*(ramsize - 16))/3;
+
+- skge_ramset(hw, rxqaddr[port], ram_addr, chunk);
+- skge_qset(skge, rxqaddr[port], skge->rx_ring.to_clean);
++ skge_ramset(hw, rxqaddr[port], ramaddr, rxspace);
++ skge_ramset(hw, txqaddr[port], ramaddr + rxspace, ramsize - rxspace);
+
++ skge_qset(skge, rxqaddr[port], skge->rx_ring.to_clean);
+ BUG_ON(skge->tx_ring.to_use != skge->tx_ring.to_clean);
+- skge_ramset(hw, txqaddr[port], ram_addr+chunk, chunk);
+ skge_qset(skge, txqaddr[port], skge->tx_ring.to_use);
+
+ /* Start receiver BMU */
+@@ -3450,15 +3450,12 @@ static int skge_reset(struct skge_hw *hw
+ if (hw->chip_id == CHIP_ID_GENESIS) {
+ if (t8 == 3) {
+ /* special case: 4 x 64k x 36, offset = 0x80000 */
+- hw->ram_size = 0x100000;
+- hw->ram_offset = 0x80000;
++ hw->ram_size = 1024;
++ hw->ram_offset = 512;
+ } else
+ hw->ram_size = t8 * 512;
+- }
+- else if (t8 == 0)
+- hw->ram_size = 0x20000;
+- else
+- hw->ram_size = t8 * 4096;
++ } else /* Yukon */
++ hw->ram_size = t8 ? t8 * 4 : 128;
+
+ hw->intr_mask = IS_HW_ERR;
+
--- /dev/null
+From stable-bounces@linux.kernel.org Tue Nov 6 14:18:53 2007
+From: Stephen Hemminger <shemminger@linux-foundation.org>
+Date: Tue, 06 Nov 2007 14:12:30 -0800
+Subject: skge: XM PHY handling fixes
+To: stable@kernel.org
+Message-ID: <20071106221308.364160885@linux-foundation.org>
+Content-Disposition: inline; filename=skge-xm-phy-poll.patch
+
+From: Stephen Hemminger <shemminger@linux-foundation.org>
+
+patch 501fb72d052d2a302b423bef7dec98d9d98c8a36 in mainline.
+
+Change how PHY is managed on SysKonnect fibre based boards.
+Poll for PHY coming up 1 per second, but use interrupt to detect loss.
+
+Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
+Signed-off-by: Jeff Garzik <jeff@garzik.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/net/skge.c | 90 ++++++++++++++++++++++++++++-------------------------
+ drivers/net/skge.h | 6 +--
+ 2 files changed, 51 insertions(+), 45 deletions(-)
+
+--- a/drivers/net/skge.c
++++ b/drivers/net/skge.c
+@@ -57,7 +57,7 @@
+ #define TX_WATCHDOG (5 * HZ)
+ #define NAPI_WEIGHT 64
+ #define BLINK_MS 250
+-#define LINK_HZ (HZ/2)
++#define LINK_HZ HZ
+
+ MODULE_DESCRIPTION("SysKonnect Gigabit Ethernet driver");
+ MODULE_AUTHOR("Stephen Hemminger <shemminger@linux-foundation.org>");
+@@ -992,19 +992,15 @@ static void xm_link_down(struct skge_hw
+ {
+ struct net_device *dev = hw->dev[port];
+ struct skge_port *skge = netdev_priv(dev);
+- u16 cmd, msk;
++ u16 cmd = xm_read16(hw, port, XM_MMU_CMD);
+
+- if (hw->phy_type == SK_PHY_XMAC) {
+- msk = xm_read16(hw, port, XM_IMSK);
+- msk |= XM_IS_INP_ASS | XM_IS_LIPA_RC | XM_IS_RX_PAGE | XM_IS_AND;
+- xm_write16(hw, port, XM_IMSK, msk);
+- }
++ xm_write16(hw, port, XM_IMSK, XM_IMSK_DISABLE);
+
+- cmd = xm_read16(hw, port, XM_MMU_CMD);
+ cmd &= ~(XM_MMU_ENA_RX | XM_MMU_ENA_TX);
+ xm_write16(hw, port, XM_MMU_CMD, cmd);
++
+ /* dummy read to ensure writing */
+- (void) xm_read16(hw, port, XM_MMU_CMD);
++ xm_read16(hw, port, XM_MMU_CMD);
+
+ if (netif_carrier_ok(dev))
+ skge_link_down(skge);
+@@ -1100,7 +1096,7 @@ static void genesis_reset(struct skge_hw
+
+ /* reset the statistics module */
+ xm_write32(hw, port, XM_GP_PORT, XM_GP_RES_STAT);
+- xm_write16(hw, port, XM_IMSK, 0xffff); /* disable XMAC IRQs */
++ xm_write16(hw, port, XM_IMSK, XM_IMSK_DISABLE);
+ xm_write32(hw, port, XM_MODE, 0); /* clear Mode Reg */
+ xm_write16(hw, port, XM_TX_CMD, 0); /* reset TX CMD Reg */
+ xm_write16(hw, port, XM_RX_CMD, 0); /* reset RX CMD Reg */
+@@ -1138,7 +1134,7 @@ static void bcom_check_link(struct skge_
+ u16 status;
+
+ /* read twice because of latch */
+- (void) xm_phy_read(hw, port, PHY_BCOM_STAT);
++ xm_phy_read(hw, port, PHY_BCOM_STAT);
+ status = xm_phy_read(hw, port, PHY_BCOM_STAT);
+
+ if ((status & PHY_ST_LSYNC) == 0) {
+@@ -1339,7 +1335,7 @@ static void xm_phy_init(struct skge_port
+ mod_timer(&skge->link_timer, jiffies + LINK_HZ);
+ }
+
+-static void xm_check_link(struct net_device *dev)
++static int xm_check_link(struct net_device *dev)
+ {
+ struct skge_port *skge = netdev_priv(dev);
+ struct skge_hw *hw = skge->hw;
+@@ -1347,25 +1343,25 @@ static void xm_check_link(struct net_dev
+ u16 status;
+
+ /* read twice because of latch */
+- (void) xm_phy_read(hw, port, PHY_XMAC_STAT);
++ xm_phy_read(hw, port, PHY_XMAC_STAT);
+ status = xm_phy_read(hw, port, PHY_XMAC_STAT);
+
+ if ((status & PHY_ST_LSYNC) == 0) {
+ xm_link_down(hw, port);
+- return;
++ return 0;
+ }
+
+ if (skge->autoneg == AUTONEG_ENABLE) {
+ u16 lpa, res;
+
+ if (!(status & PHY_ST_AN_OVER))
+- return;
++ return 0;
+
+ lpa = xm_phy_read(hw, port, PHY_XMAC_AUNE_LP);
+ if (lpa & PHY_B_AN_RF) {
+ printk(KERN_NOTICE PFX "%s: remote fault\n",
+ dev->name);
+- return;
++ return 0;
+ }
+
+ res = xm_phy_read(hw, port, PHY_XMAC_RES_ABI);
+@@ -1381,7 +1377,7 @@ static void xm_check_link(struct net_dev
+ default:
+ printk(KERN_NOTICE PFX "%s: duplex mismatch\n",
+ dev->name);
+- return;
++ return 0;
+ }
+
+ /* We are using IEEE 802.3z/D5.0 Table 37-4 */
+@@ -1405,11 +1401,14 @@ static void xm_check_link(struct net_dev
+
+ if (!netif_carrier_ok(dev))
+ genesis_link_up(skge);
++ return 1;
+ }
+
+ /* Poll to check for link coming up.
++ *
+ * Since internal PHY is wired to a level triggered pin, can't
+- * get an interrupt when carrier is detected.
++ * get an interrupt when carrier is detected, need to poll for
++ * link coming up.
+ */
+ static void xm_link_timer(unsigned long arg)
+ {
+@@ -1417,29 +1416,35 @@ static void xm_link_timer(unsigned long
+ struct net_device *dev = skge->netdev;
+ struct skge_hw *hw = skge->hw;
+ int port = skge->port;
++ int i;
++ unsigned long flags;
+
+ if (!netif_running(dev))
+ return;
+
+- if (netif_carrier_ok(dev)) {
++ spin_lock_irqsave(&hw->phy_lock, flags);
++
++ /*
++ * Verify that the link by checking GPIO register three times.
++ * This pin has the signal from the link_sync pin connected to it.
++ */
++ for (i = 0; i < 3; i++) {
++ if (xm_read16(hw, port, XM_GP_PORT) & XM_GP_INP_ASS)
++ goto link_down;
++ }
++
++ /* Re-enable interrupt to detect link down */
++ if (xm_check_link(dev)) {
++ u16 msk = xm_read16(hw, port, XM_IMSK);
++ msk &= ~XM_IS_INP_ASS;
++ xm_write16(hw, port, XM_IMSK, msk);
+ xm_read16(hw, port, XM_ISRC);
+- if (!(xm_read16(hw, port, XM_ISRC) & XM_IS_INP_ASS))
+- goto nochange;
+ } else {
+- if (xm_read32(hw, port, XM_GP_PORT) & XM_GP_INP_ASS)
+- goto nochange;
+- xm_read16(hw, port, XM_ISRC);
+- if (xm_read16(hw, port, XM_ISRC) & XM_IS_INP_ASS)
+- goto nochange;
++link_down:
++ mod_timer(&skge->link_timer,
++ round_jiffies(jiffies + LINK_HZ));
+ }
+-
+- spin_lock(&hw->phy_lock);
+- xm_check_link(dev);
+- spin_unlock(&hw->phy_lock);
+-
+-nochange:
+- if (netif_running(dev))
+- mod_timer(&skge->link_timer, jiffies + LINK_HZ);
++ spin_unlock_irqrestore(&hw->phy_lock, flags);
+ }
+
+ static void genesis_mac_init(struct skge_hw *hw, int port)
+@@ -1683,14 +1688,16 @@ static void genesis_mac_intr(struct skge
+ printk(KERN_DEBUG PFX "%s: mac interrupt status 0x%x\n",
+ skge->netdev->name, status);
+
+- if (hw->phy_type == SK_PHY_XMAC &&
+- (status & (XM_IS_INP_ASS | XM_IS_LIPA_RC)))
+- xm_link_down(hw, port);
++ if (hw->phy_type == SK_PHY_XMAC && (status & XM_IS_INP_ASS)) {
++ xm_link_down(hw, port);
++ mod_timer(&skge->link_timer, jiffies + 1);
++ }
+
+ if (status & XM_IS_TXF_UR) {
+ xm_write32(hw, port, XM_MODE, XM_MD_FTF);
+ ++skge->net_stats.tx_fifo_errors;
+ }
++
+ if (status & XM_IS_RXF_OV) {
+ xm_write32(hw, port, XM_MODE, XM_MD_FRF);
+ ++skge->net_stats.rx_fifo_errors;
+@@ -1750,11 +1757,12 @@ static void genesis_link_up(struct skge_
+ }
+
+ xm_write32(hw, port, XM_MODE, mode);
+- msk = XM_DEF_MSK;
+- if (hw->phy_type != SK_PHY_XMAC)
+- msk |= XM_IS_INP_ASS; /* disable GP0 interrupt bit */
+
++ /* Turn on detection of Tx underrun, Rx overrun */
++ msk = xm_read16(hw, port, XM_IMSK);
++ msk &= ~(XM_IS_RXF_OV | XM_IS_TXF_UR);
+ xm_write16(hw, port, XM_IMSK, msk);
++
+ xm_read16(hw, port, XM_ISRC);
+
+ /* get MMU Command Reg. */
+@@ -2185,7 +2193,7 @@ static void yukon_mac_intr(struct skge_h
+ u8 status = skge_read8(hw, SK_REG(port, GMAC_IRQ_SRC));
+
+ if (netif_msg_intr(skge))
+- printk(KERN_DEBUG PFX "%s: mac interrupt status 0x%x\n",
++ printk(KERN_DEBUG PFX "%s: yukon mac interrupt status 0x%x\n",
+ dev->name, status);
+
+ if (status & GM_IS_RX_FF_OR) {
+--- a/drivers/net/skge.h
++++ b/drivers/net/skge.h
+@@ -2193,11 +2193,9 @@ enum {
+ XM_IS_TXF_UR = 1<<2, /* Bit 2: Transmit FIFO Underrun */
+ XM_IS_TX_COMP = 1<<1, /* Bit 1: Frame Tx Complete */
+ XM_IS_RX_COMP = 1<<0, /* Bit 0: Frame Rx Complete */
+-};
+-
+-#define XM_DEF_MSK (~(XM_IS_INP_ASS | XM_IS_LIPA_RC | \
+- XM_IS_RXF_OV | XM_IS_TXF_UR))
+
++ XM_IMSK_DISABLE = 0xffff,
++};
+
+ /* XM_HW_CFG 16 bit r/w Hardware Config Register */
+ enum {
--- /dev/null
+From stable-bounces@linux.kernel.org Tue Nov 6 14:19:17 2007
+From: Stephen Hemminger <shemminger@linux-foundation.org>
+Date: Tue, 06 Nov 2007 14:12:32 -0800
+Subject: sky2: ethtool register reserved area blackout
+To: stable@kernel.org
+Message-ID: <20071106221308.515605644@linux-foundation.org>
+Content-Disposition: inline; filename=sky2-ethdump.patch
+
+From: Stephen Hemminger <shemminger@linux-foundation.org>
+
+patch 295b54c4902c52cd00d7c837d50a86e39e26caec in mainline.
+
+Make sure and not dump reserved areas of device space.
+Touching some of these causes machine check exceptions on boards
+like D-Link DGE-550SX.
+
+Coding note, used a complex switch statement rather than bitmap
+because it is easier to relate the block values to the documentation
+rather than looking at a encoded bitmask.
+
+Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/net/sky2.c | 62 +++++++++++++++++++++++++++++++++++++++++++++--------
+ 1 file changed, 53 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/sky2.c
++++ b/drivers/net/sky2.c
+@@ -3570,20 +3570,64 @@ static void sky2_get_regs(struct net_dev
+ {
+ const struct sky2_port *sky2 = netdev_priv(dev);
+ const void __iomem *io = sky2->hw->regs;
++ unsigned int b;
+
+ regs->version = 1;
+- memset(p, 0, regs->len);
+
+- memcpy_fromio(p, io, B3_RAM_ADDR);
+-
+- /* skip diagnostic ram region */
+- memcpy_fromio(p + B3_RI_WTO_R1, io + B3_RI_WTO_R1, 0x2000 - B3_RI_WTO_R1);
++ for (b = 0; b < 128; b++) {
++ /* This complicated switch statement is to make sure and
++ * only access regions that are unreserved.
++ * Some blocks are only valid on dual port cards.
++ * and block 3 has some special diagnostic registers that
++ * are poison.
++ */
++ switch (b) {
++ case 3:
++ /* skip diagnostic ram region */
++ memcpy_fromio(p + 0x10, io + 0x10, 128 - 0x10);
++ break;
+
+- /* copy GMAC registers */
+- memcpy_fromio(p + BASE_GMAC_1, io + BASE_GMAC_1, 0x1000);
+- if (sky2->hw->ports > 1)
+- memcpy_fromio(p + BASE_GMAC_2, io + BASE_GMAC_2, 0x1000);
++ /* dual port cards only */
++ case 5: /* Tx Arbiter 2 */
++ case 9: /* RX2 */
++ case 14 ... 15: /* TX2 */
++ case 17: case 19: /* Ram Buffer 2 */
++ case 22 ... 23: /* Tx Ram Buffer 2 */
++ case 25: /* Rx MAC Fifo 1 */
++ case 27: /* Tx MAC Fifo 2 */
++ case 31: /* GPHY 2 */
++ case 40 ... 47: /* Pattern Ram 2 */
++ case 52: case 54: /* TCP Segmentation 2 */
++ case 112 ... 116: /* GMAC 2 */
++ if (sky2->hw->ports == 1)
++ goto reserved;
++ /* fall through */
++ case 0: /* Control */
++ case 2: /* Mac address */
++ case 4: /* Tx Arbiter 1 */
++ case 7: /* PCI express reg */
++ case 8: /* RX1 */
++ case 12 ... 13: /* TX1 */
++ case 16: case 18:/* Rx Ram Buffer 1 */
++ case 20 ... 21: /* Tx Ram Buffer 1 */
++ case 24: /* Rx MAC Fifo 1 */
++ case 26: /* Tx MAC Fifo 1 */
++ case 28 ... 29: /* Descriptor and status unit */
++ case 30: /* GPHY 1*/
++ case 32 ... 39: /* Pattern Ram 1 */
++ case 48: case 50: /* TCP Segmentation 1 */
++ case 56 ... 60: /* PCI space */
++ case 80 ... 84: /* GMAC 1 */
++ memcpy_fromio(p, io, 128);
++ break;
++ default:
++reserved:
++ memset(p, 0, 128);
++ }
+
++ p += 128;
++ io += 128;
++ }
+ }
+
+ /* In order to do Jumbo packets on these chips, need to turn off the
--- /dev/null
+From stable-bounces@linux.kernel.org Tue Nov 6 14:18:53 2007
+From: Stephen Hemminger <shemminger@linux-foundation.org>
+Date: Tue, 06 Nov 2007 14:12:33 -0800
+Subject: sky2: fix power settings on Yukon XL
+To: stable@kernel.org
+Message-ID: <20071106221308.578043016@linux-foundation.org>
+Content-Disposition: inline; filename=sky2-phy-power.patch
+
+From: Stephen Hemminger <shemminger@linux-foundation.org>
+
+patch ff35164e72648e0bf0b10ec4410c195e8607e88b in mainline.
+
+Make sure PCI register for PHY power gets set correctly.
+
+Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/net/sky2.c | 13 +++++++------
+ 1 file changed, 7 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/sky2.c
++++ b/drivers/net/sky2.c
+@@ -603,21 +603,22 @@ static void sky2_phy_init(struct sky2_hw
+ static void sky2_phy_power(struct sky2_hw *hw, unsigned port, int onoff)
+ {
+ u32 reg1;
+- static const u32 phy_power[]
+- = { PCI_Y2_PHY1_POWD, PCI_Y2_PHY2_POWD };
+-
+- /* looks like this XL is back asswards .. */
+- if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1)
+- onoff = !onoff;
++ static const u32 phy_power[] = { PCI_Y2_PHY1_POWD, PCI_Y2_PHY2_POWD };
++ static const u32 coma_mode[] = { PCI_Y2_PHY1_COMA, PCI_Y2_PHY2_COMA };
+
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+ reg1 = sky2_pci_read32(hw, PCI_DEV_REG1);
++
+ if (onoff)
+ /* Turn off phy power saving */
+ reg1 &= ~phy_power[port];
+ else
+ reg1 |= phy_power[port];
+
++ if (onoff && hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1)
++ reg1 |= coma_mode[port];
++
++
+ sky2_pci_write32(hw, PCI_DEV_REG1, reg1);
+ sky2_pci_read32(hw, PCI_DEV_REG1);
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
--- /dev/null
+From stable-bounces@linux.kernel.org Tue Nov 6 14:19:11 2007
+From: Stephen Hemminger <shemminger@linux-foundation.org>
+Date: Tue, 06 Nov 2007 14:12:34 -0800
+Subject: sky2: status ring race fix
+To: stable@kernel.org
+Message-ID: <20071106221308.629175444@linux-foundation.org>
+Content-Disposition: inline; filename=sky2-status-race.patch
+
+patch ab5adecb2d02f3688719dfb5936a82833fcc3955 in mainline.
+
+The D-Link PCI-X board (and maybe others) can lie about status
+ring entries. It seems it will update the register for last status
+index before completing the DMA for the ring entry. To avoid reading
+stale data, zap the old entry and check.
+
+Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
+Signed-off-by: Jeff Garzik <jeff@garzik.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/net/sky2.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/sky2.c
++++ b/drivers/net/sky2.c
+@@ -2246,20 +2246,26 @@ static int sky2_status_intr(struct sky2_
+ while (hw->st_idx != hwidx) {
+ struct sky2_port *sky2;
+ struct sky2_status_le *le = hw->st_le + hw->st_idx;
+- unsigned port = le->css & CSS_LINK_BIT;
++ unsigned port;
+ struct net_device *dev;
+ struct sk_buff *skb;
+ u32 status;
+ u16 length;
++ u8 opcode = le->opcode;
++
++ if (!(opcode & HW_OWNER))
++ break;
+
+ hw->st_idx = RING_NEXT(hw->st_idx, STATUS_RING_SIZE);
+
++ port = le->css & CSS_LINK_BIT;
+ dev = hw->dev[port];
+ sky2 = netdev_priv(dev);
+ length = le16_to_cpu(le->length);
+ status = le32_to_cpu(le->status);
+
+- switch (le->opcode & ~HW_OWNER) {
++ le->opcode = 0;
++ switch (opcode & ~HW_OWNER) {
+ case OP_RXSTAT:
+ ++rx[port];
+ skb = sky2_receive(dev, length, status);
+@@ -2352,7 +2358,7 @@ static int sky2_status_intr(struct sky2_
+ default:
+ if (net_ratelimit())
+ printk(KERN_WARNING PFX
+- "unknown status opcode 0x%x\n", le->opcode);
++ "unknown status opcode 0x%x\n", opcode);
+ }
+ }
+