Hi Stephen, list;
Patch attached backports the current sky2 driver in GIT to 2.6.15-1.
This version works, I don't know if I'm missing anything crucial in
the port.
It also seems to alleviate problems we have been experiencing (and
are well documented in various forums/lists online) where the ethernet
device would become unresponsive under moderate to heavy loads and
require restarting the interface to come back up. I found a lot of
(recent) discourse about these problems and as such figured the port
might have some use in the public domain.
I apologize in advance if gmail incorrectly mails my patch.
Cheers,
Tom Burns
Software Developer
International Datacasting
http://www.intldata.ca
--- /home/tburns/kernel-git/linux-2.6/drivers/net/sky2.c 2007-04-18 15:55:08.000000000 -0400
+++ ./sky2-patched.c 2007-04-19 14:55:09.000000000 -0400
@@ -1034,13 +1034,13 @@
struct sky2_hw *hw = sky2->hw;
u16 port = sky2->port;
- netif_tx_lock_bh(dev);
+ spin_lock_bh(&sky2->tx_lock);
sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_ON);
sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_ON);
sky2->vlgrp = grp;
- netif_tx_unlock_bh(dev);
+ spin_unlock_bh(&sky2->tx_lock);
}
static void sky2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
@@ -1049,13 +1049,16 @@
struct sky2_hw *hw = sky2->hw;
u16 port = sky2->port;
- netif_tx_lock_bh(dev);
+ spin_lock_bh(&sky2->tx_lock);
sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_OFF);
sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_OFF);
- vlan_group_set_device(sky2->vlgrp, vid, NULL);
- netif_tx_unlock_bh(dev);
+ if (sky2->vlgrp)
+ sky2->vlgrp->vlan_devices[vid] = NULL;
+
+
+ spin_unlock_bh(&sky2->tx_lock);
}
#endif
@@ -1074,7 +1077,7 @@
unsigned long p;
int i;
- skb = netdev_alloc_skb(sky2->netdev, sky2->rx_data_size + RX_SKB_ALIGN);
+ skb = alloc_skb(sky2->rx_data_size + RX_SKB_ALIGN, GFP_ATOMIC);
if (!skb)
goto nomem;
@@ -1138,7 +1141,7 @@
thresh = (size - 8) / sizeof(u32);
/* Account for overhead of skb - to avoid order > 0 allocation */
- space = SKB_DATA_ALIGN(size) + NET_SKB_PAD
+ space = SKB_DATA_ALIGN(size) + 16
+ sizeof(struct skb_shared_info);
sky2->rx_nfrags = space >> PAGE_SHIFT;
@@ -1334,10 +1337,10 @@
count = sizeof(dma_addr_t) / sizeof(u32);
count += skb_shinfo(skb)->nr_frags * count;
- if (skb_is_gso(skb))
+ if (skb_shinfo(skb)->tso_size)
++count;
- if (skb->ip_summed == CHECKSUM_PARTIAL)
+ if (skb->ip_summed == CHECKSUM_HW)
++count;
return count;
@@ -1361,8 +1364,15 @@
u16 mss;
u8 ctrl;
- if (unlikely(tx_avail(sky2) < tx_le_req(skb)))
+
+ if (!spin_trylock(&sky2->tx_lock))
+ return NETDEV_TX_LOCKED;
+
+ if (unlikely(tx_avail(sky2) < tx_le_req(skb))) {
+ spin_unlock(&sky2->tx_lock);
+
return NETDEV_TX_BUSY;
+ }
if (unlikely(netif_msg_tx_queued(sky2)))
printk(KERN_DEBUG "%s: tx queued, slot %u, len %d\n",
@@ -1381,7 +1391,7 @@
}
/* Check for TCP Segmentation Offload */
- mss = skb_shinfo(skb)->gso_size;
+ mss = skb_shinfo(skb)->tso_size;
if (mss != 0) {
mss += ((skb->h.th->doff - 5) * 4); /* TCP options */
mss += (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr);
@@ -1411,12 +1421,12 @@
#endif
/* Handle TCP checksum offload */
- if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ if (skb->ip_summed == CHECKSUM_HW) {
unsigned offset = skb->h.raw - skb->data;
u32 tcpsum;
tcpsum = offset << 16; /* sum start */
- tcpsum |= offset + skb->csum_offset; /* sum write */
+ tcpsum |= offset + (u32) skb->csum; /* sum write */
ctrl = CALSUM | WR_SUM | INIT_SUM | LOCK_SUM;
if (skb->nh.iph->protocol == IPPROTO_UDP)
@@ -1478,6 +1488,8 @@
sky2_put_idx(hw, txqaddr[sky2->port], sky2->tx_prod);
dev->trans_start = jiffies;
+ spin_unlock(&sky2->tx_lock);
+
return NETDEV_TX_OK;
}
@@ -1538,9 +1550,11 @@
{
struct sky2_port *sky2 = netdev_priv(dev);
- netif_tx_lock_bh(dev);
+ spin_lock_bh(&sky2->tx_lock);
+
sky2_tx_complete(sky2, sky2->tx_prod);
- netif_tx_unlock_bh(dev);
+
+ spin_unlock_bh(&sky2->tx_lock);
}
/* Network shutdown */
@@ -1954,7 +1968,8 @@
{
struct sk_buff *skb;
- skb = netdev_alloc_skb(sky2->netdev, length + 2);
+ skb = alloc_skb(length + 2, GFP_ATOMIC);
+
if (likely(skb)) {
skb_reserve(skb, 2);
pci_dma_sync_single_for_cpu(sky2->hw->pdev, re->data_addr,
@@ -2058,8 +2073,9 @@
if (length < copybreak)
skb = receive_copy(sky2, re, length);
- else
+ else
skb = receive_new(sky2, re, length);
+
resubmit:
sky2_rx_submit(sky2, re);
@@ -2092,9 +2108,9 @@
struct sky2_port *sky2 = netdev_priv(dev);
if (netif_running(dev)) {
- netif_tx_lock(dev);
+ spin_lock_bh(&sky2->tx_lock);
sky2_tx_complete(sky2, last);
- netif_tx_unlock(dev);
+ spin_unlock_bh(&sky2->tx_lock);
}
}
@@ -2134,6 +2150,7 @@
sky2->net_stats.rx_packets++;
sky2->net_stats.rx_bytes += skb->len;
dev->last_rx = jiffies;
+ skb->dev = sky2->netdev;
#ifdef SKY2_VLAN_TAG_USED
if (sky2->vlgrp && (status & GMR_FS_VLAN)) {
@@ -2176,7 +2193,7 @@
*/
if (likely(status >> 16 == (status & 0xffff))) {
skb = sky2->rx_ring[sky2->rx_next].skb;
- skb->ip_summed = CHECKSUM_COMPLETE;
+ skb->ip_summed = CHECKSUM_HW;
skb->csum = status & 0xffff;
} else {
printk(KERN_NOTICE PFX "%s: hardware receive "
@@ -2431,7 +2448,7 @@
}
}
-static irqreturn_t sky2_intr(int irq, void *dev_id)
+static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs)
{
struct sky2_hw *hw = dev_id;
struct net_device *dev0 = hw->dev[0];
@@ -3404,6 +3421,8 @@
sky2->hw = hw;
sky2->msg_enable = netif_msg_init(debug, default_msg);
+ spin_lock_init(&sky2->tx_lock);
+
/* Auto speed and flow control */
sky2->autoneg = AUTONEG_ENABLE;
sky2->flow_mode = FC_BOTH;
@@ -3455,7 +3474,7 @@
}
/* Handle software interrupt used during MSI test */
-static irqreturn_t __devinit sky2_test_intr(int irq, void *dev_id)
+static irqreturn_t __devinit sky2_test_intr(int irq, void *dev_id, struct pt_regs *regs)
{
struct sky2_hw *hw = dev_id;
u32 status = sky2_read32(hw, B0_Y2_SP_ISRC2);
@@ -3627,7 +3646,7 @@
goto err_out_free_netdev;
}
- err = request_irq(pdev->irq, sky2_intr, hw->msi ? 0 : IRQF_SHARED,
+ err = request_irq(pdev->irq, sky2_intr, hw->msi ? 0 : SA_SHIRQ,
dev->name, hw);
if (err) {
dev_err(&pdev->dev, "cannot assign irq %d\n", pdev->irq);
@@ -3653,7 +3672,7 @@
}
setup_timer(&hw->idle_timer, sky2_idle, (unsigned long) hw);
- INIT_WORK(&hw->restart_work, sky2_restart);
+ INIT_WORK(&hw->restart_work, sky2_restart, &hw->restart_work);
sky2_idle_start(hw);
--- /home/tburns/kernel-git/linux-2.6/drivers/net/sky2.h 2007-04-18 15:55:08.000000000 -0400
+++ ./sky2-patched.h 2007-04-19 14:57:40.000000000 -0400
@@ -1880,6 +1880,8 @@
u32 msg_enable;
spinlock_t phy_lock;
+ spinlock_t tx_lock ____cacheline_aligned_in_smp;
+
struct tx_ring_info *tx_ring;
struct sky2_tx_le *tx_le;
u16 tx_cons; /* next le to check */