[PATCH] mv643xx_eth: Hold spinlocks only where needed

This driver has historically held a spin_lock during the entire open
and stop functions and while receiving multiple packets.  This is
unecessarily long and holds locks during calls that may sleep.
This patch reduces the size of windows where locks are held.

Signed-off-by: Dale Farnsworth <dale@farnsworth.org>

 mv643xx_eth.c |  172 ++++++++++++++++++++++++++++++----------------------------
 1 file changed, 91 insertions(+), 81 deletions(-)
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
This commit is contained in:
Dale Farnsworth 2006-01-16 16:56:30 -07:00 committed by Jeff Garzik
parent dd09b1de08
commit 8f5187035a
1 changed files with 92 additions and 82 deletions

View File

@ -129,15 +129,8 @@ static inline void mv_write(int offset, u32 data)
*/ */
static int mv643xx_eth_change_mtu(struct net_device *dev, int new_mtu) static int mv643xx_eth_change_mtu(struct net_device *dev, int new_mtu)
{ {
struct mv643xx_private *mp = netdev_priv(dev); if ((new_mtu > 9500) || (new_mtu < 64))
unsigned long flags;
spin_lock_irqsave(&mp->lock, flags);
if ((new_mtu > 9500) || (new_mtu < 64)) {
spin_unlock_irqrestore(&mp->lock, flags);
return -EINVAL; return -EINVAL;
}
dev->mtu = new_mtu; dev->mtu = new_mtu;
/* /*
@ -157,7 +150,6 @@ static int mv643xx_eth_change_mtu(struct net_device *dev, int new_mtu)
dev->name); dev->name);
} }
spin_unlock_irqrestore(&mp->lock, flags);
return 0; return 0;
} }
@ -353,8 +345,6 @@ static int mv643xx_eth_free_tx_queue(struct net_device *dev,
if (!(eth_int_cause_ext & (BIT0 | BIT8))) if (!(eth_int_cause_ext & (BIT0 | BIT8)))
return released; return released;
spin_lock(&mp->lock);
/* Check only queue 0 */ /* Check only queue 0 */
while (eth_tx_return_desc(mp, &pkt_info) == ETH_OK) { while (eth_tx_return_desc(mp, &pkt_info) == ETH_OK) {
if (pkt_info.cmd_sts & BIT0) { if (pkt_info.cmd_sts & BIT0) {
@ -377,8 +367,6 @@ static int mv643xx_eth_free_tx_queue(struct net_device *dev,
} }
} }
spin_unlock(&mp->lock);
return released; return released;
} }
@ -518,6 +506,8 @@ static irqreturn_t mv643xx_eth_int_handler(int irq, void *dev_id,
mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num), 0); mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num), 0);
mv_write(MV643XX_ETH_INTERRUPT_EXTEND_MASK_REG mv_write(MV643XX_ETH_INTERRUPT_EXTEND_MASK_REG
(port_num), 0); (port_num), 0);
/* ensure previous writes have taken effect */
mv_read(MV643XX_ETH_INTERRUPT_EXTEND_MASK_REG(port_num));
__netif_rx_schedule(dev); __netif_rx_schedule(dev);
} }
#else #else
@ -533,6 +523,9 @@ static irqreturn_t mv643xx_eth_int_handler(int irq, void *dev_id,
/* Unmask all interrupts on ethernet port */ /* Unmask all interrupts on ethernet port */
mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num), mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num),
INT_CAUSE_MASK_ALL); INT_CAUSE_MASK_ALL);
/* wait for previous write to take effect */
mv_read(MV643XX_ETH_INTERRUPT_MASK_REG(port_num));
queue_task(&mp->rx_task, &tq_immediate); queue_task(&mp->rx_task, &tq_immediate);
mark_bh(IMMEDIATE_BH); mark_bh(IMMEDIATE_BH);
#else #else
@ -657,34 +650,20 @@ static int mv643xx_eth_open(struct net_device *dev)
unsigned int port_num = mp->port_num; unsigned int port_num = mp->port_num;
int err; int err;
spin_lock_irq(&mp->lock);
err = request_irq(dev->irq, mv643xx_eth_int_handler, err = request_irq(dev->irq, mv643xx_eth_int_handler,
SA_SHIRQ | SA_SAMPLE_RANDOM, dev->name, dev); SA_SHIRQ | SA_SAMPLE_RANDOM, dev->name, dev);
if (err) { if (err) {
printk(KERN_ERR "Can not assign IRQ number to MV643XX_eth%d\n", printk(KERN_ERR "Can not assign IRQ number to MV643XX_eth%d\n",
port_num); port_num);
err = -EAGAIN; return -EAGAIN;
goto out;
} }
if (mv643xx_eth_real_open(dev)) { if (mv643xx_eth_real_open(dev)) {
printk("%s: Error opening interface\n", dev->name); printk("%s: Error opening interface\n", dev->name);
free_irq(dev->irq, dev);
err = -EBUSY; err = -EBUSY;
goto out_free;
} }
spin_unlock_irq(&mp->lock);
return 0;
out_free:
free_irq(dev->irq, dev);
out:
spin_unlock_irq(&mp->lock);
return err; return err;
} }
@ -790,18 +769,6 @@ static int mv643xx_eth_real_open(struct net_device *dev)
/* Stop RX Queues */ /* Stop RX Queues */
mv_write(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_REG(port_num), 0x0000ff00); mv_write(MV643XX_ETH_RECEIVE_QUEUE_COMMAND_REG(port_num), 0x0000ff00);
/* Clear the ethernet port interrupts */
mv_write(MV643XX_ETH_INTERRUPT_CAUSE_REG(port_num), 0);
mv_write(MV643XX_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0);
/* Unmask RX buffer and TX end interrupt */
mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num),
INT_CAUSE_UNMASK_ALL);
/* Unmask phy and link status changes interrupts */
mv_write(MV643XX_ETH_INTERRUPT_EXTEND_MASK_REG(port_num),
INT_CAUSE_UNMASK_ALL_EXT);
/* Set the MAC Address */ /* Set the MAC Address */
memcpy(mp->port_mac_addr, dev->dev_addr, 6); memcpy(mp->port_mac_addr, dev->dev_addr, 6);
@ -903,8 +870,17 @@ static int mv643xx_eth_real_open(struct net_device *dev)
mp->tx_int_coal = mp->tx_int_coal =
eth_port_set_tx_coal(port_num, 133000000, MV643XX_TX_COAL); eth_port_set_tx_coal(port_num, 133000000, MV643XX_TX_COAL);
netif_start_queue(dev); /* Clear any pending ethernet port interrupts */
mv_write(MV643XX_ETH_INTERRUPT_CAUSE_REG(port_num), 0);
mv_write(MV643XX_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0);
/* Unmask phy and link status changes interrupts */
mv_write(MV643XX_ETH_INTERRUPT_EXTEND_MASK_REG(port_num),
INT_CAUSE_UNMASK_ALL_EXT);
/* Unmask RX buffer and TX end interrupt */
mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num),
INT_CAUSE_UNMASK_ALL);
return 0; return 0;
} }
@ -983,37 +959,38 @@ static int mv643xx_eth_real_stop(struct net_device *dev)
struct mv643xx_private *mp = netdev_priv(dev); struct mv643xx_private *mp = netdev_priv(dev);
unsigned int port_num = mp->port_num; unsigned int port_num = mp->port_num;
netif_carrier_off(dev);
netif_stop_queue(dev);
mv643xx_eth_free_tx_rings(dev);
mv643xx_eth_free_rx_rings(dev);
eth_port_reset(mp->port_num);
/* Disable ethernet port interrupts */
mv_write(MV643XX_ETH_INTERRUPT_CAUSE_REG(port_num), 0);
mv_write(MV643XX_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0);
/* Mask RX buffer and TX end interrupt */ /* Mask RX buffer and TX end interrupt */
mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num), 0); mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num), 0);
/* Mask phy and link status changes interrupts */ /* Mask phy and link status changes interrupts */
mv_write(MV643XX_ETH_INTERRUPT_EXTEND_MASK_REG(port_num), 0); mv_write(MV643XX_ETH_INTERRUPT_EXTEND_MASK_REG(port_num), 0);
/* ensure previous writes have taken effect */
mv_read(MV643XX_ETH_INTERRUPT_MASK_REG(port_num));
#ifdef MV643XX_NAPI
netif_poll_disable(dev);
#endif
netif_carrier_off(dev);
netif_stop_queue(dev);
eth_port_reset(mp->port_num);
mv643xx_eth_free_tx_rings(dev);
mv643xx_eth_free_rx_rings(dev);
#ifdef MV643XX_NAPI
netif_poll_enable(dev);
#endif
return 0; return 0;
} }
static int mv643xx_eth_stop(struct net_device *dev) static int mv643xx_eth_stop(struct net_device *dev)
{ {
struct mv643xx_private *mp = netdev_priv(dev);
spin_lock_irq(&mp->lock);
mv643xx_eth_real_stop(dev); mv643xx_eth_real_stop(dev);
free_irq(dev->irq, dev); free_irq(dev->irq, dev);
spin_unlock_irq(&mp->lock);
return 0; return 0;
} }
@ -1053,14 +1030,11 @@ static int mv643xx_poll(struct net_device *dev, int *budget)
struct mv643xx_private *mp = netdev_priv(dev); struct mv643xx_private *mp = netdev_priv(dev);
int done = 1, orig_budget, work_done; int done = 1, orig_budget, work_done;
unsigned int port_num = mp->port_num; unsigned int port_num = mp->port_num;
unsigned long flags;
#ifdef MV643XX_TX_FAST_REFILL #ifdef MV643XX_TX_FAST_REFILL
if (++mp->tx_clean_threshold > 5) { if (++mp->tx_clean_threshold > 5) {
spin_lock_irqsave(&mp->lock, flags);
mv643xx_tx(dev); mv643xx_tx(dev);
mp->tx_clean_threshold = 0; mp->tx_clean_threshold = 0;
spin_unlock_irqrestore(&mp->lock, flags);
} }
#endif #endif
@ -1078,15 +1052,13 @@ static int mv643xx_poll(struct net_device *dev, int *budget)
} }
if (done) { if (done) {
spin_lock_irqsave(&mp->lock, flags); netif_rx_complete(dev);
__netif_rx_complete(dev);
mv_write(MV643XX_ETH_INTERRUPT_CAUSE_REG(port_num), 0); mv_write(MV643XX_ETH_INTERRUPT_CAUSE_REG(port_num), 0);
mv_write(MV643XX_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0); mv_write(MV643XX_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0);
mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num), mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num),
INT_CAUSE_UNMASK_ALL); INT_CAUSE_UNMASK_ALL);
mv_write(MV643XX_ETH_INTERRUPT_EXTEND_MASK_REG(port_num), mv_write(MV643XX_ETH_INTERRUPT_EXTEND_MASK_REG(port_num),
INT_CAUSE_UNMASK_ALL_EXT); INT_CAUSE_UNMASK_ALL_EXT);
spin_unlock_irqrestore(&mp->lock, flags);
} }
return done ? 0 : 1; return done ? 0 : 1;
@ -2687,6 +2659,7 @@ static ETH_FUNC_RET_STATUS eth_port_send(struct mv643xx_private *mp,
struct eth_tx_desc *current_descriptor; struct eth_tx_desc *current_descriptor;
struct eth_tx_desc *first_descriptor; struct eth_tx_desc *first_descriptor;
u32 command; u32 command;
unsigned long flags;
/* Do not process Tx ring in case of Tx ring resource error */ /* Do not process Tx ring in case of Tx ring resource error */
if (mp->tx_resource_err) if (mp->tx_resource_err)
@ -2703,6 +2676,8 @@ static ETH_FUNC_RET_STATUS eth_port_send(struct mv643xx_private *mp,
return ETH_ERROR; return ETH_ERROR;
} }
spin_lock_irqsave(&mp->lock, flags);
mp->tx_ring_skbs++; mp->tx_ring_skbs++;
BUG_ON(mp->tx_ring_skbs > mp->tx_ring_size); BUG_ON(mp->tx_ring_skbs > mp->tx_ring_size);
@ -2752,11 +2727,15 @@ static ETH_FUNC_RET_STATUS eth_port_send(struct mv643xx_private *mp,
mp->tx_resource_err = 1; mp->tx_resource_err = 1;
mp->tx_curr_desc_q = tx_first_desc; mp->tx_curr_desc_q = tx_first_desc;
spin_unlock_irqrestore(&mp->lock, flags);
return ETH_QUEUE_LAST_RESOURCE; return ETH_QUEUE_LAST_RESOURCE;
} }
mp->tx_curr_desc_q = tx_next_desc; mp->tx_curr_desc_q = tx_next_desc;
spin_unlock_irqrestore(&mp->lock, flags);
return ETH_OK; return ETH_OK;
} }
#else #else
@ -2767,11 +2746,14 @@ static ETH_FUNC_RET_STATUS eth_port_send(struct mv643xx_private *mp,
int tx_desc_used; int tx_desc_used;
struct eth_tx_desc *current_descriptor; struct eth_tx_desc *current_descriptor;
unsigned int command_status; unsigned int command_status;
unsigned long flags;
/* Do not process Tx ring in case of Tx ring resource error */ /* Do not process Tx ring in case of Tx ring resource error */
if (mp->tx_resource_err) if (mp->tx_resource_err)
return ETH_QUEUE_FULL; return ETH_QUEUE_FULL;
spin_lock_irqsave(&mp->lock, flags);
mp->tx_ring_skbs++; mp->tx_ring_skbs++;
BUG_ON(mp->tx_ring_skbs > mp->tx_ring_size); BUG_ON(mp->tx_ring_skbs > mp->tx_ring_size);
@ -2802,9 +2784,12 @@ static ETH_FUNC_RET_STATUS eth_port_send(struct mv643xx_private *mp,
/* Check for ring index overlap in the Tx desc ring */ /* Check for ring index overlap in the Tx desc ring */
if (tx_desc_curr == tx_desc_used) { if (tx_desc_curr == tx_desc_used) {
mp->tx_resource_err = 1; mp->tx_resource_err = 1;
spin_unlock_irqrestore(&mp->lock, flags);
return ETH_QUEUE_LAST_RESOURCE; return ETH_QUEUE_LAST_RESOURCE;
} }
spin_unlock_irqrestore(&mp->lock, flags);
return ETH_OK; return ETH_OK;
} }
#endif #endif
@ -2827,23 +2812,27 @@ static ETH_FUNC_RET_STATUS eth_port_send(struct mv643xx_private *mp,
* Tx ring 'first' and 'used' indexes are updated. * Tx ring 'first' and 'used' indexes are updated.
* *
* RETURN: * RETURN:
* ETH_ERROR in case the routine can not access Tx desc ring. * ETH_OK on success
* ETH_RETRY in case there is transmission in process. * ETH_ERROR otherwise.
* ETH_END_OF_JOB if the routine has nothing to release.
* ETH_OK otherwise.
* *
*/ */
static ETH_FUNC_RET_STATUS eth_tx_return_desc(struct mv643xx_private *mp, static ETH_FUNC_RET_STATUS eth_tx_return_desc(struct mv643xx_private *mp,
struct pkt_info *p_pkt_info) struct pkt_info *p_pkt_info)
{ {
int tx_desc_used; int tx_desc_used;
#ifdef MV643XX_CHECKSUM_OFFLOAD_TX int tx_busy_desc;
int tx_busy_desc = mp->tx_first_desc_q;
#else
int tx_busy_desc = mp->tx_curr_desc_q;
#endif
struct eth_tx_desc *p_tx_desc_used; struct eth_tx_desc *p_tx_desc_used;
unsigned int command_status; unsigned int command_status;
unsigned long flags;
int err = ETH_OK;
spin_lock_irqsave(&mp->lock, flags);
#ifdef MV643XX_CHECKSUM_OFFLOAD_TX
tx_busy_desc = mp->tx_first_desc_q;
#else
tx_busy_desc = mp->tx_curr_desc_q;
#endif
/* Get the Tx Desc ring indexes */ /* Get the Tx Desc ring indexes */
tx_desc_used = mp->tx_used_desc_q; tx_desc_used = mp->tx_used_desc_q;
@ -2851,18 +2840,24 @@ static ETH_FUNC_RET_STATUS eth_tx_return_desc(struct mv643xx_private *mp,
p_tx_desc_used = &mp->p_tx_desc_area[tx_desc_used]; p_tx_desc_used = &mp->p_tx_desc_area[tx_desc_used];
/* Sanity check */ /* Sanity check */
if (p_tx_desc_used == NULL) if (p_tx_desc_used == NULL) {
return ETH_ERROR; err = ETH_ERROR;
goto out;
}
/* Stop release. About to overlap the current available Tx descriptor */ /* Stop release. About to overlap the current available Tx descriptor */
if (tx_desc_used == tx_busy_desc && !mp->tx_resource_err) if (tx_desc_used == tx_busy_desc && !mp->tx_resource_err) {
return ETH_END_OF_JOB; err = ETH_ERROR;
goto out;
}
command_status = p_tx_desc_used->cmd_sts; command_status = p_tx_desc_used->cmd_sts;
/* Still transmitting... */ /* Still transmitting... */
if (command_status & (ETH_BUFFER_OWNED_BY_DMA)) if (command_status & (ETH_BUFFER_OWNED_BY_DMA)) {
return ETH_RETRY; err = ETH_ERROR;
goto out;
}
/* Pass the packet information to the caller */ /* Pass the packet information to the caller */
p_pkt_info->cmd_sts = command_status; p_pkt_info->cmd_sts = command_status;
@ -2880,7 +2875,10 @@ static ETH_FUNC_RET_STATUS eth_tx_return_desc(struct mv643xx_private *mp,
BUG_ON(mp->tx_ring_skbs == 0); BUG_ON(mp->tx_ring_skbs == 0);
mp->tx_ring_skbs--; mp->tx_ring_skbs--;
return ETH_OK; out:
spin_unlock_irqrestore(&mp->lock, flags);
return err;
} }
/* /*
@ -2912,11 +2910,14 @@ static ETH_FUNC_RET_STATUS eth_port_receive(struct mv643xx_private *mp,
int rx_next_curr_desc, rx_curr_desc, rx_used_desc; int rx_next_curr_desc, rx_curr_desc, rx_used_desc;
volatile struct eth_rx_desc *p_rx_desc; volatile struct eth_rx_desc *p_rx_desc;
unsigned int command_status; unsigned int command_status;
unsigned long flags;
/* Do not process Rx ring in case of Rx ring resource error */ /* Do not process Rx ring in case of Rx ring resource error */
if (mp->rx_resource_err) if (mp->rx_resource_err)
return ETH_QUEUE_FULL; return ETH_QUEUE_FULL;
spin_lock_irqsave(&mp->lock, flags);
/* Get the Rx Desc ring 'curr and 'used' indexes */ /* Get the Rx Desc ring 'curr and 'used' indexes */
rx_curr_desc = mp->rx_curr_desc_q; rx_curr_desc = mp->rx_curr_desc_q;
rx_used_desc = mp->rx_used_desc_q; rx_used_desc = mp->rx_used_desc_q;
@ -2928,8 +2929,10 @@ static ETH_FUNC_RET_STATUS eth_port_receive(struct mv643xx_private *mp,
rmb(); rmb();
/* Nothing to receive... */ /* Nothing to receive... */
if (command_status & (ETH_BUFFER_OWNED_BY_DMA)) if (command_status & (ETH_BUFFER_OWNED_BY_DMA)) {
spin_unlock_irqrestore(&mp->lock, flags);
return ETH_END_OF_JOB; return ETH_END_OF_JOB;
}
p_pkt_info->byte_cnt = (p_rx_desc->byte_cnt) - RX_BUF_OFFSET; p_pkt_info->byte_cnt = (p_rx_desc->byte_cnt) - RX_BUF_OFFSET;
p_pkt_info->cmd_sts = command_status; p_pkt_info->cmd_sts = command_status;
@ -2949,6 +2952,8 @@ static ETH_FUNC_RET_STATUS eth_port_receive(struct mv643xx_private *mp,
if (rx_next_curr_desc == rx_used_desc) if (rx_next_curr_desc == rx_used_desc)
mp->rx_resource_err = 1; mp->rx_resource_err = 1;
spin_unlock_irqrestore(&mp->lock, flags);
return ETH_OK; return ETH_OK;
} }
@ -2977,6 +2982,9 @@ static ETH_FUNC_RET_STATUS eth_rx_return_buff(struct mv643xx_private *mp,
{ {
int used_rx_desc; /* Where to return Rx resource */ int used_rx_desc; /* Where to return Rx resource */
volatile struct eth_rx_desc *p_used_rx_desc; volatile struct eth_rx_desc *p_used_rx_desc;
unsigned long flags;
spin_lock_irqsave(&mp->lock, flags);
/* Get 'used' Rx descriptor */ /* Get 'used' Rx descriptor */
used_rx_desc = mp->rx_used_desc_q; used_rx_desc = mp->rx_used_desc_q;
@ -3000,6 +3008,8 @@ static ETH_FUNC_RET_STATUS eth_rx_return_buff(struct mv643xx_private *mp,
/* Any Rx return cancels the Rx resource error status */ /* Any Rx return cancels the Rx resource error status */
mp->rx_resource_err = 0; mp->rx_resource_err = 0;
spin_unlock_irqrestore(&mp->lock, flags);
return ETH_OK; return ETH_OK;
} }