Skge driver update
Subsystems (2)
cover Skge driver update
Update to skge driver:
1-3 bug fixes for upstream-fixes
4-7 improvements for later releases
PATCH 1/7 skge: deadlock on tx timeout
The skge driver will deadlock if gets a transmit timeout because the netif_tx_lock() is already held.
--- drivers/net/skge.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/net/skge.c +4 -2
@@ -2576,7 +2576,11 @@ static int skge_down(struct net_device *skge_led (skge,LED_MODE_OFF );netif_poll_disable (dev); + +netif_tx_lock_bh (dev);skge_tx_clean (dev); +netif_tx_unlock_bh (dev); +skge_rx_clean (skge);kfree (skge->rx_ring.start);
@@ -2721,7 +2725,6 @@ static void skge_tx_clean(struct net_devstruct skge_port *skge =netdev_priv (dev);struct skge_element *e; -netif_tx_lock_bh (dev);for (e = skge->tx_ring.to_clean; e != skge->tx_ring.to_use; e = e->next) {struct skge_tx_desc *td = e->desc;skge_tx_free (skge, e, td->control);
@@ -2730,7 +2733,6 @@ static void skge_tx_clean(struct net_dev skge->tx_ring.to_clean = e;netif_wake_queue (dev); -netif_tx_unlock_bh (dev); }static void skge_tx_timeout (struct net_device *dev)
Re: [PATCH 1/7] skge: deadlock on tx timeout
Quoted 4 lines · Stephen Hemminger
The skge driver will deadlock if gets a transmit timeout because the
netif_tx_lock()is already held.Signed-off-by Stephen Hemminger <[email protected]>
applied 1-3 to #upstream-fixes
PATCH 2/7 skge: mask irqs when device down
In reply to Stephen HemmingerWheen a port on the skge driver is not used, it should mask off interrupts from theat port.
--- drivers/net/skge.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-)
drivers/net/skge.c +17 -7
@@ -105,7 +105,8 @@ static const int txqaddr[] = { Q_XA1, Q_static const int rxqaddr[] = {Q_R1 ,Q_R2 };static const u32 rxirqmask[] = {IS_R1_F ,IS_R2_F };static const u32 txirqmask[] = {IS_XA1_F ,IS_XA2_F }; -static const u32 irqmask[] = {IS_R1_F |IS_XA1_F ,IS_R2_F |IS_XA2_F }; +static const u32 napimask[] = {IS_R1_F |IS_XA1_F ,IS_R2_F |IS_XA2_F }; +static const u32 portmask[] = {IS_PORT_1 ,IS_PORT_2 };static int skge_get_regs_len (struct net_device *dev) {
@@ -2504,6 +2505,11 @@ static int skge_up(struct net_device *deskge_write8 (hw,Q_ADDR (rxqaddr[port],Q_CSR ),CSR_START |CSR_IRQ_CL_F );skge_led (skge,LED_MODE_ON ); +spin_lock_irq (&hw->hw_lock); + hw->intr_mask |= portmask[port]; +skge_write32 (hw,B0_IMSK , hw->intr_mask); +spin_unlock_irq (&hw->hw_lock); +netif_poll_enable (dev);return 0 ;
@@ -2533,6 +2539,13 @@ static int skge_down(struct net_device *if (hw->chip_id ==CHIP_ID_GENESIS && hw->phy_type ==SK_PHY_XMAC )cancel_delayed_work (&skge->link_thread); +netif_poll_disable (dev); + +spin_lock_irq (&hw->hw_lock); + hw->intr_mask &= ~portmask[port]; +skge_write32 (hw,B0_IMSK , hw->intr_mask); +spin_unlock_irq (&hw->hw_lock); +skge_write8 (skge->hw,SK_REG (skge->port,LNK_LED_REG ),LED_OFF );if (hw->chip_id ==CHIP_ID_GENESIS )genesis_stop (skge);
@@ -2575,8 +2588,6 @@ static int skge_down(struct net_device *skge_led (skge,LED_MODE_OFF ); -netif_poll_disable (dev); -netif_tx_lock_bh (dev);skge_tx_clean (dev);netif_tx_unlock_bh (dev);
@@ -3051,7 +3062,7 @@ static int skge_poll(struct net_device *spin_lock_irqsave (&hw->hw_lock, flags);__netif_rx_complete (dev); - hw->intr_mask |= irqmask[skge->port]; + hw->intr_mask |= napimask[skge->port];skge_write32 (hw,B0_IMSK , hw->intr_mask);skge_read32 (hw,B0_IMSK );spin_unlock_irqrestore (&hw->hw_lock, flags);
@@ -3415,10 +3426,9 @@ static int skge_reset(struct skge_hw *hwelse hw->ram_size = t8 *4096 ; - hw->intr_mask =IS_HW_ERR |IS_PORT_1 ; -if (hw->ports >1 ) - hw->intr_mask |=IS_PORT_2 ; + hw->intr_mask =IS_HW_ERR ; +/* Use PHY IRQ for all but fiber based Genesis board */ if (!(hw->chip_id ==CHIP_ID_GENESIS && hw->phy_type ==SK_PHY_XMAC )) hw->intr_mask |=IS_EXT_REG ;
PATCH 3/7 skge: use per-port phy locking
In reply to Stephen HemmingerRather than a workqueue and a per-board mutex to control PHY, use a tasklet and spinlock. Tasklet is lower overhead and works just as well for this.
--- drivers/net/skge.c | 82 ++++++++++++++++++++++++++++------------------------- drivers/net/skge.h | 6 +-- 2 files changed, 47 insertions(+), 41 deletions(-)
drivers/net/skge.c +44 -38
@@ -672,7 +672,7 @@ static void skge_led(struct skge_port *sstruct skge_hw *hw = skge->hw;int port = skge->port; -mutex_lock (&hw->phy_mutex); +spin_lock_bh (&hw->phy_lock);if (hw->chip_id ==CHIP_ID_GENESIS ) {switch (mode) {case LED_MODE_OFF :
@@ -743,7 +743,7 @@ static void skge_led(struct skge_port *s PHY_M_LED_MO_RX(MO_LED_ON)); } } -mutex_unlock (&hw->phy_mutex); +spin_unlock_bh (&hw->phy_lock); }/* blink LED's for finding board */
@@ -1317,7 +1317,7 @@ static void xm_phy_init(struct skge_portxm_phy_write (hw, port,PHY_XMAC_CTRL , ctrl);/* Poll PHY for status changes */ -schedule_delayed_work (&skge->link_thread,LINK_HZ ); +mod_timer (&skge->link_timer, jiffies +LINK_HZ ); }static void xm_check_link (struct net_device *dev)
@@ -1392,10 +1392,9 @@ static void xm_check_link(struct net_dev* Since internal PHY is wired to a level triggered pin, can't * get an interrupt when carrier is detected. */ -static void xm_link_timer (struct work_struct *work) +static void xm_link_timer (unsigned long arg) { -struct skge_port *skge = - container_of(work, struct skge_port, link_thread.work); +struct skge_port *skge = (struct skge_port *) arg;struct net_device *dev = skge->netdev;struct skge_hw *hw = skge->hw;int port = skge->port;
@@ -1415,13 +1414,13 @@ static void xm_link_timer(struct work_st goto nochange; } -mutex_lock (&hw->phy_mutex); +spin_lock (&hw->phy_lock);xm_check_link (dev); -mutex_unlock (&hw->phy_mutex); +spin_unlock (&hw->phy_lock); nochange:if (netif_running (dev)) -schedule_delayed_work (&skge->link_thread,LINK_HZ ); +mod_timer (&skge->link_timer, jiffies +LINK_HZ ); }static void genesis_mac_init (struct skge_hw *hw,int port)
@@ -2324,7 +2323,7 @@ static void skge_phy_reset(struct skge_pnetif_stop_queue (skge->netdev);netif_carrier_off (skge->netdev); -mutex_lock (&hw->phy_mutex); +spin_lock_bh (&hw->phy_lock);if (hw->chip_id ==CHIP_ID_GENESIS ) {genesis_reset (hw, port);genesis_mac_init (hw, port);
@@ -2332,7 +2331,7 @@ static void skge_phy_reset(struct skge_pyukon_reset (hw, port);yukon_init (hw, port); } -mutex_unlock (&hw->phy_mutex); +spin_unlock_bh (&hw->phy_lock); dev->set_multicast_list (dev); }
@@ -2355,12 +2354,12 @@ static int skge_ioctl(struct net_device/* fallthru */ case SIOCGMIIREG : {u16 val =0 ; -mutex_lock (&hw->phy_mutex); +spin_lock_bh (&hw->phy_lock);if (hw->chip_id ==CHIP_ID_GENESIS ) err =__xm_phy_read (hw, skge->port, data->reg_num &0x1f , &val);else err =__gm_phy_read (hw, skge->port, data->reg_num &0x1f , &val); -mutex_unlock (&hw->phy_mutex); +spin_unlock_bh (&hw->phy_lock); data->val_out = val;break ; }
@@ -2369,14 +2368,14 @@ static int skge_ioctl(struct net_deviceif (!capable (CAP_NET_ADMIN ))return -EPERM ; -mutex_lock (&hw->phy_mutex); +spin_lock_bh (&hw->phy_lock);if (hw->chip_id ==CHIP_ID_GENESIS ) err =xm_phy_write (hw, skge->port, data->reg_num &0x1f , data->val_in);else err =gm_phy_write (hw, skge->port, data->reg_num &0x1f , data->val_in); -mutex_unlock (&hw->phy_mutex); +spin_unlock_bh (&hw->phy_lock);break ; }return err;
@@ -2482,12 +2481,12 @@ static int skge_up(struct net_device *de goto free_rx_ring;/* Initialize MAC */ -mutex_lock (&hw->phy_mutex); +spin_lock_bh (&hw->phy_lock);if (hw->chip_id ==CHIP_ID_GENESIS )genesis_mac_init (hw, port);else yukon_mac_init (hw, port); -mutex_unlock (&hw->phy_mutex); +spin_unlock_bh (&hw->phy_lock);/* Configure RAMbuffers */ chunk = hw->ram_size / ((hw->ports +1 )*2 );
@@ -2537,7 +2536,7 @@ static int skge_down(struct net_device *netif_stop_queue (dev);if (hw->chip_id ==CHIP_ID_GENESIS && hw->phy_type ==SK_PHY_XMAC ) -cancel_delayed_work (&skge->link_thread); +del_timer_sync (&skge->link_timer);netif_poll_disable (dev);
@@ -3173,28 +3172,29 @@ static void skge_error_irq(struct skge_h }/* -* Interrupt from PHY are handled in work queue +* Interrupt from PHY are handled in tasklet (softirq) * because accessing phy registers requires spin wait which might * cause excess interrupt latency. */ -static void skge_extirq (struct work_struct *work) +static void skge_extirq (unsigned long arg) { - struct skge_hw *hw = container_of(work, struct skge_hw, phy_work); +struct skge_hw *hw = (struct skge_hw *) arg;int port; -mutex_lock (&hw->phy_mutex);for (port =0 ; port < hw->ports; port++) {struct net_device *dev = hw->dev[port]; -struct skge_port *skge =netdev_priv (dev);if (netif_running (dev)) { +struct skge_port *skge =netdev_priv (dev); + +spin_lock (&hw->phy_lock);if (hw->chip_id !=CHIP_ID_GENESIS )yukon_phy_intr (skge);else if (hw->phy_type ==SK_PHY_BCOM )bcom_phy_intr (skge); +spin_unlock (&hw->phy_lock); } } -mutex_unlock (&hw->phy_mutex);spin_lock_irq (&hw->hw_lock); hw->intr_mask |=IS_EXT_REG ;
@@ -3219,7 +3219,7 @@ static irqreturn_t skge_intr(int irq, vo status &= hw->intr_mask;if (status &IS_EXT_REG ) { hw->intr_mask &= ~IS_EXT_REG ; -schedule_work (&hw->phy_work); +tasklet_schedule (&hw->phy_task); }if (status & (IS_XA1_F |IS_R1_F )) {
@@ -3295,23 +3295,28 @@ static int skge_set_mac_address(struct nmemcpy (dev->dev_addr, addr->sa_data,ETH_ALEN ); -/* disable Rx */ - ctrl =gma_read16 (hw, port,GM_GP_CTRL ); -gma_write16 (hw, port,GM_GP_CTRL , ctrl & ~GM_GPCR_RX_ENA ); +if (!netif_running (dev)) { +memcpy_toio (hw->regs +B2_MAC_1 + port*8 , dev->dev_addr,ETH_ALEN ); +memcpy_toio (hw->regs +B2_MAC_2 + port*8 , dev->dev_addr,ETH_ALEN ); + }else { +/* disable Rx */ +spin_lock_bh (&hw->phy_lock); + ctrl =gma_read16 (hw, port,GM_GP_CTRL ); +gma_write16 (hw, port,GM_GP_CTRL , ctrl & ~GM_GPCR_RX_ENA ); -memcpy_toio (hw->regs +B2_MAC_1 + port*8 , dev->dev_addr,ETH_ALEN ); -memcpy_toio (hw->regs +B2_MAC_2 + port*8 , dev->dev_addr,ETH_ALEN ); +memcpy_toio (hw->regs +B2_MAC_1 + port*8 , dev->dev_addr,ETH_ALEN ); +memcpy_toio (hw->regs +B2_MAC_2 + port*8 , dev->dev_addr,ETH_ALEN ); -if (netif_running (dev)) {if (hw->chip_id ==CHIP_ID_GENESIS )xm_outaddr (hw, port,XM_SA , dev->dev_addr);else {gma_set_addr (hw, port,GM_SRC_ADDR_1L , dev->dev_addr);gma_set_addr (hw, port,GM_SRC_ADDR_2L , dev->dev_addr); } - } -gma_write16 (hw, port,GM_GP_CTRL , ctrl); +gma_write16 (hw, port,GM_GP_CTRL , ctrl); +spin_unlock_bh (&hw->phy_lock); + }return 0 ; }
@@ -3496,14 +3501,12 @@ static int skge_reset(struct skge_hw *hwskge_write32 (hw,B0_IMSK , hw->intr_mask); -mutex_lock (&hw->phy_mutex);for (i =0 ; i < hw->ports; i++) {if (hw->chip_id ==CHIP_ID_GENESIS )genesis_reset (hw, i);else yukon_reset (hw, i); } -mutex_unlock (&hw->phy_mutex);return 0 ; }
@@ -3551,6 +3554,7 @@ static struct net_device *skge_devinit(s skge->netdev = dev; skge->hw = hw; skge->msg_enable =netif_msg_init (debug, default_msg); + skge->tx_ring.count =DEFAULT_TX_RING_SIZE ; skge->rx_ring.count =DEFAULT_RX_RING_SIZE ;
@@ -3567,7 +3571,7 @@ static struct net_device *skge_devinit(s skge->port = port;/* Only used for Genesis XMAC */ -INIT_DELAYED_WORK (&skge->link_thread, xm_link_timer); +setup_timer (&skge->link_timer, xm_link_timer, (unsigned long ) skge);if (hw->chip_id !=CHIP_ID_GENESIS ) { dev->features |=NETIF_F_IP_CSUM |NETIF_F_SG ;
@@ -3649,9 +3653,9 @@ static int __devinit skge_probe(struct p } hw->pdev = pdev; -mutex_init (&hw->phy_mutex); -INIT_WORK (&hw->phy_work, skge_extirq);spin_lock_init (&hw->hw_lock); +spin_lock_init (&hw->phy_lock); +tasklet_init (&hw->phy_task, &skge_extirq, (unsigned long ) hw); hw->regs =ioremap_nocache (pci_resource_start (pdev,0 ),0x4000 );if (!hw->regs) {
@@ -3737,6 +3741,8 @@ static void __devexit skge_remove(struct dev0 = hw->dev[0 ];unregister_netdev (dev0); +tasklet_disable (&hw->phy_task); +spin_lock_irq (&hw->hw_lock); hw->intr_mask =0 ;skge_write32 (hw,B0_IMSK ,0 );
drivers/net/skge.h +3 -3
@@ -2424,8 +2424,8 @@ struct skge_hw {u32 ram_size;u32 ram_offset;u16 phy_addr; -struct work_struct phy_work; -struct mutex phy_mutex; +spinlock_t phy_lock; +struct tasklet_struct phy_task; };enum pause_control {
@@ -2457,7 +2457,7 @@ struct skge_port {struct net_device_stats net_stats; -struct delayed_work link_thread; +struct timer_list link_timer;enum pause_control flow_control;enum pause_status flow_status;u8 rx_csum;
Re: [PATCH 3/7] skge: use per-port phy locking
Quoted 5 lines · Stephen Hemminger
Rather than a workqueue and a per-board mutex to control PHY, use a tasklet and spinlock. Tasklet is lower overhead and works just as well for this.
Signed-off-by Stephen Hemminger <[email protected]>
Like we give a crap about overhead of PHY code.
This seems like the wrong direction to me, but let's see where this leads.
Jeff
PATCH 4/7 skge: ignore unused error interrupts
In reply to Stephen HemmingerThe following hardware error bits only show up on Genesis chipset and are handled elsewhere, so they can be masked off.
--- drivers/net/skge.h | 1 - 1 file changed, 1 deletion(-)
drivers/net/skge.h +0 -1
@@ -232,7 +232,6 @@ enum {IS_R2_PAR_ERR =1 <<0 ,/* Queue R2 Parity Error */ IS_ERR_MSK =IS_IRQ_MST_ERR |IS_IRQ_STAT - |IS_NO_STAT_M1 |IS_NO_STAT_M2 |IS_RAM_RD_PAR |IS_RAM_WR_PAR |IS_M1_PAR_ERR |IS_M2_PAR_ERR | IS_R1_PAR_ERR | IS_R2_PAR_ERR,
Re: [PATCH 4/7] skge: ignore unused error interrupts
Quoted 8 lines · Stephen Hemminger
The following hardware error bits only show up on Genesis chipset and are handled elsewhere, so they can be masked off.
Signed-off-by Stephen Hemminger <[email protected]>--- drivers/net/skge.h | 1 - 1 file changed, 1 deletion(-)
applied 4-7
PATCH 5/7 skge: transmit locking improvements
In reply to Stephen HemmingerDon't need to lock when processing transmit complete unless queue fills. Modeled after tg3.
--- drivers/net/skge.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-)
drivers/net/skge.c +18 -10
@@ -2602,6 +2602,7 @@ static int skge_down(struct net_device *static inline int skge_avail (const struct skge_ring *ring) { +smp_mb ();return ((ring->to_clean > ring->to_use) ?0 : ring->count) + (ring->to_clean - ring->to_use) -1 ; }
@@ -2690,6 +2691,8 @@ static int skge_xmit_frame(struct sk_buf dev->name, e - skge->tx_ring.start, skb->len); skge->tx_ring.to_use = e->next; +smp_wmb (); +if (skge_avail (&skge->tx_ring) <=TX_LOW_WATER ) {pr_debug ("%s: transmit queue full\n" , dev->name);netif_stop_queue (dev);
@@ -2707,8 +2710,6 @@ static void skge_tx_free(struct skge_por {struct pci_dev *pdev = skge->hw->pdev; -BUG_ON (!e->skb); -/* skb header vs. fragment */ if (control &BMU_STF ) pci_unmap_single(pdev, pci_unmap_addr(e, mapaddr),
@@ -2726,7 +2727,6 @@ static void skge_tx_free(struct skge_pordev_kfree_skb (e->skb); } - e->skb =NULL ; }/* Free all buffers in transmit ring */
@@ -2998,21 +2998,29 @@ static void skge_tx_done(struct net_deviskge_write8 (skge->hw,Q_ADDR (txqaddr[skge->port],Q_CSR ),CSR_IRQ_CL_F ); -netif_tx_lock (dev);for (e = ring->to_clean; e != ring->to_use; e = e->next) { -struct skge_tx_desc *td = e->desc; +u32 control = ((const struct skge_tx_desc *) e->desc)->control; -if (td->control &BMU_OWN ) +if (control &BMU_OWN )break ; -skge_tx_free (skge, e, td->control); +skge_tx_free (skge, e, control); } skge->tx_ring.to_clean = e; -if (skge_avail (&skge->tx_ring) >TX_LOW_WATER ) -netif_wake_queue (dev); +/* Can run lockless until we need to synchronize to restart queue. */ +smp_mb (); + +if (unlikely (netif_queue_stopped (dev) && +skge_avail (&skge->tx_ring) >TX_LOW_WATER )) { +netif_tx_lock (dev); +if (unlikely (netif_queue_stopped (dev) && +skge_avail (&skge->tx_ring) >TX_LOW_WATER )) { +netif_wake_queue (dev); -netif_tx_unlock (dev); + } +netif_tx_unlock (dev); + } }static int skge_poll (struct net_device *dev,int *budget)
PATCH 6/7 skge: rearrange fields
In reply to Stephen HemmingerDo some minor rearrangement of data structures to try and optimize cache usage.
--- drivers/net/skge.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/net/skge.h +5 -4
@@ -2446,15 +2446,15 @@ enum pause_status {struct skge_port { -u32 msg_enable;struct skge_hw *hw;struct net_device *netdev;int port; +u32 msg_enable;struct skge_ring tx_ring; -struct skge_ring rx_ring; -struct net_device_stats net_stats; +struct skge_ring rx_ring____cacheline_aligned_in_smp ; +unsigned int rx_buf_size;struct timer_list link_timer;enum pause_control flow_control;
@@ -2470,7 +2470,8 @@ struct skge_port {void *mem;/* PCI memory for rings */ dma_addr_t dma;unsigned long mem_size; -unsigned int rx_buf_size; + +struct net_device_stats net_stats; };
PATCH 7/7 skge: version 1.11
In reply to Stephen HemmingerNew version to track changes.
--- drivers/net/skge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/skge.c +1 -1
@@ -42,7 +42,7 @@#include "skge.h" #define DRV_NAME "skge" -#define DRV_VERSION "1.10" +#define DRV_VERSION "1.11"#define PFX DRV_NAME " "#define DEFAULT_TX_RING_SIZE 128