Commit ff561eef authored by Dale Farnsworth's avatar Dale Farnsworth Committed by Jeff Garzik

[PATCH] mv643xx_eth: Refactor/clean up tx queue handling

Signed-off-by: default avatarDale Farnsworth <dale@farnsworth.org>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent c8aaea25
...@@ -62,6 +62,9 @@ ...@@ -62,6 +62,9 @@
#define WRAP HW_IP_ALIGN + ETH_HLEN + VLAN_HLEN + FCS_LEN #define WRAP HW_IP_ALIGN + ETH_HLEN + VLAN_HLEN + FCS_LEN
#define RX_SKB_SIZE ((dev->mtu + WRAP + 7) & ~0x7) #define RX_SKB_SIZE ((dev->mtu + WRAP + 7) & ~0x7)
#define ETH_RX_QUEUES_ENABLED (1 << 0) /* use only Q0 for receive */
#define ETH_TX_QUEUES_ENABLED (1 << 0) /* use only Q0 for transmit */
#define INT_UNMASK_ALL 0x0007ffff #define INT_UNMASK_ALL 0x0007ffff
#define INT_UNMASK_ALL_EXT 0x0011ffff #define INT_UNMASK_ALL_EXT 0x0011ffff
#define INT_MASK_ALL 0x00000000 #define INT_MASK_ALL 0x00000000
...@@ -333,49 +336,78 @@ static void mv643xx_eth_tx_timeout_task(struct net_device *dev) ...@@ -333,49 +336,78 @@ static void mv643xx_eth_tx_timeout_task(struct net_device *dev)
netif_device_attach(dev); netif_device_attach(dev);
} }
/* /**
* mv643xx_eth_free_tx_queue * mv643xx_eth_free_tx_descs - Free the tx desc data for completed descriptors
*
* Input : dev - a pointer to the required interface
* *
* Output : 0 if was able to release skb , nonzero otherwise * If force is non-zero, frees uncompleted descriptors as well
*/ */
static int mv643xx_eth_free_tx_queue(struct net_device *dev, int mv643xx_eth_free_tx_descs(struct net_device *dev, int force)
unsigned int eth_int_cause_ext)
{ {
struct mv643xx_private *mp = netdev_priv(dev); struct mv643xx_private *mp = netdev_priv(dev);
struct net_device_stats *stats = &mp->stats; struct eth_tx_desc *desc;
struct pkt_info pkt_info; u32 cmd_sts;
int released = 1; struct sk_buff *skb;
unsigned long flags;
int tx_index;
dma_addr_t addr;
int count;
int released = 0;
if (!(eth_int_cause_ext & (BIT0 | BIT8))) while (mp->tx_desc_count > 0) {
return released; spin_lock_irqsave(&mp->lock, flags);
tx_index = mp->tx_used_desc_q;
desc = &mp->p_tx_desc_area[tx_index];
cmd_sts = desc->cmd_sts;
if (!force && (cmd_sts & ETH_BUFFER_OWNED_BY_DMA)) {
spin_unlock_irqrestore(&mp->lock, flags);
return released;
}
mp->tx_used_desc_q = (tx_index + 1) % mp->tx_ring_size;
mp->tx_desc_count--;
addr = desc->buf_ptr;
count = desc->byte_cnt;
skb = mp->tx_skb[tx_index];
if (skb)
mp->tx_skb[tx_index] = NULL;
spin_unlock_irqrestore(&mp->lock, flags);
/* Check only queue 0 */ if (cmd_sts & BIT0) {
while (eth_tx_return_desc(mp, &pkt_info) == ETH_OK) {
if (pkt_info.cmd_sts & BIT0) {
printk("%s: Error in TX\n", dev->name); printk("%s: Error in TX\n", dev->name);
stats->tx_errors++; mp->stats.tx_errors++;
} }
if (pkt_info.cmd_sts & ETH_TX_FIRST_DESC) if (cmd_sts & ETH_TX_FIRST_DESC)
dma_unmap_single(NULL, pkt_info.buf_ptr, dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE);
pkt_info.byte_cnt,
DMA_TO_DEVICE);
else else
dma_unmap_page(NULL, pkt_info.buf_ptr, dma_unmap_page(NULL, addr, count, DMA_TO_DEVICE);
pkt_info.byte_cnt,
DMA_TO_DEVICE);
if (pkt_info.return_info) { if (skb)
dev_kfree_skb_irq(pkt_info.return_info); dev_kfree_skb_irq(skb);
released = 0;
} released = 1;
} }
return released; return released;
} }
static void mv643xx_eth_free_completed_tx_descs(struct net_device *dev)
{
struct mv643xx_private *mp = netdev_priv(dev);
if (mv643xx_eth_free_tx_descs(dev, 0) &&
mp->tx_ring_size - mp->tx_desc_count >= MAX_DESCS_PER_SKB)
netif_wake_queue(dev);
}
static void mv643xx_eth_free_all_tx_descs(struct net_device *dev)
{
mv643xx_eth_free_tx_descs(dev, 1);
}
/* /*
* mv643xx_eth_receive * mv643xx_eth_receive
* *
...@@ -547,15 +579,13 @@ static irqreturn_t mv643xx_eth_int_handler(int irq, void *dev_id, ...@@ -547,15 +579,13 @@ static irqreturn_t mv643xx_eth_int_handler(int irq, void *dev_id,
*/ */
mv_write(MV643XX_ETH_INTERRUPT_CAUSE_REG(port_num), mv_write(MV643XX_ETH_INTERRUPT_CAUSE_REG(port_num),
~eth_int_cause); ~eth_int_cause);
if (eth_int_cause_ext != 0x0) if (eth_int_cause_ext != 0x0) {
mv_write(MV643XX_ETH_INTERRUPT_CAUSE_EXTEND_REG mv_write(MV643XX_ETH_INTERRUPT_CAUSE_EXTEND_REG
(port_num), ~eth_int_cause_ext); (port_num), ~eth_int_cause_ext);
/* UDP change : We may need this */
/* UDP change : We may need this */ if (eth_int_cause_ext & (BIT0 | BIT8))
if ((eth_int_cause_ext & 0x0000ffff) && mv643xx_eth_free_completed_tx_descs(dev);
(mv643xx_eth_free_tx_queue(dev, eth_int_cause_ext) == 0) && }
(mp->tx_ring_size - mp->tx_desc_count > MAX_DESCS_PER_SKB))
netif_wake_queue(dev);
#ifdef MV643XX_NAPI #ifdef MV643XX_NAPI
} else { } else {
if (netif_rx_schedule_prep(dev)) { if (netif_rx_schedule_prep(dev)) {
...@@ -596,14 +626,13 @@ static irqreturn_t mv643xx_eth_int_handler(int irq, void *dev_id, ...@@ -596,14 +626,13 @@ static irqreturn_t mv643xx_eth_int_handler(int irq, void *dev_id,
if (mii_link_ok(&mp->mii)) { if (mii_link_ok(&mp->mii)) {
mii_ethtool_gset(&mp->mii, &cmd); mii_ethtool_gset(&mp->mii, &cmd);
mv643xx_eth_update_pscr(dev, &cmd); mv643xx_eth_update_pscr(dev, &cmd);
mv643xx_eth_port_enable_tx(port_num,
ETH_TX_QUEUES_ENABLED);
if (!netif_carrier_ok(dev)) { if (!netif_carrier_ok(dev)) {
netif_carrier_on(dev); netif_carrier_on(dev);
if (mp->tx_ring_size - mp->tx_desc_count > if (mp->tx_ring_size - mp->tx_desc_count >=
MAX_DESCS_PER_SKB) { MAX_DESCS_PER_SKB)
netif_wake_queue(dev); netif_wake_queue(dev);
/* Start TX queue */
mv643xx_eth_port_enable_tx(port_num, mp->port_tx_queue_command);
}
} }
} else if (netif_carrier_ok(dev)) { } else if (netif_carrier_ok(dev)) {
netif_stop_queue(dev); netif_stop_queue(dev);
...@@ -735,9 +764,6 @@ static void ether_init_rx_desc_ring(struct mv643xx_private *mp) ...@@ -735,9 +764,6 @@ static void ether_init_rx_desc_ring(struct mv643xx_private *mp)
mp->rx_used_desc_q = 0; mp->rx_used_desc_q = 0;
mp->rx_desc_area_size = rx_desc_num * sizeof(struct eth_rx_desc); mp->rx_desc_area_size = rx_desc_num * sizeof(struct eth_rx_desc);
/* Enable queue 0 for this port */
mp->port_rx_queue_command = 1;
} }
/* /*
...@@ -779,9 +805,6 @@ static void ether_init_tx_desc_ring(struct mv643xx_private *mp) ...@@ -779,9 +805,6 @@ static void ether_init_tx_desc_ring(struct mv643xx_private *mp)
mp->tx_used_desc_q = 0; mp->tx_used_desc_q = 0;
mp->tx_desc_area_size = tx_desc_num * sizeof(struct eth_tx_desc); mp->tx_desc_area_size = tx_desc_num * sizeof(struct eth_tx_desc);
/* Enable queue 0 for this port */
mp->port_tx_queue_command = 1;
} }
static int mv643xx_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) static int mv643xx_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
...@@ -963,25 +986,14 @@ out_free_irq: ...@@ -963,25 +986,14 @@ out_free_irq:
static void mv643xx_eth_free_tx_rings(struct net_device *dev) static void mv643xx_eth_free_tx_rings(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 curr;
struct sk_buff *skb;
/* Stop Tx Queues */ /* Stop Tx Queues */
mv643xx_eth_port_disable_tx(port_num); mv643xx_eth_port_disable_tx(mp->port_num);
/* Free outstanding skb's on TX rings */ /* Free outstanding skb's on TX ring */
for (curr = 0; mp->tx_desc_count && curr < mp->tx_ring_size; curr++) { mv643xx_eth_free_all_tx_descs(dev);
skb = mp->tx_skb[curr];
if (skb) { BUG_ON(mp->tx_used_desc_q != mp->tx_curr_desc_q);
mp->tx_desc_count -= skb_shinfo(skb)->nr_frags;
dev_kfree_skb(skb);
mp->tx_desc_count--;
}
}
if (mp->tx_desc_count)
printk("%s: Error on Tx descriptor free - could not free %d"
" descriptors\n", dev->name, mp->tx_desc_count);
/* Free TX ring */ /* Free TX ring */
if (mp->tx_sram_size) if (mp->tx_sram_size)
...@@ -1062,30 +1074,6 @@ static int mv643xx_eth_stop(struct net_device *dev) ...@@ -1062,30 +1074,6 @@ static int mv643xx_eth_stop(struct net_device *dev)
} }
#ifdef MV643XX_NAPI #ifdef MV643XX_NAPI
static void mv643xx_tx(struct net_device *dev)
{
struct mv643xx_private *mp = netdev_priv(dev);
struct pkt_info pkt_info;
while (eth_tx_return_desc(mp, &pkt_info) == ETH_OK) {
if (pkt_info.cmd_sts & ETH_TX_FIRST_DESC)
dma_unmap_single(NULL, pkt_info.buf_ptr,
pkt_info.byte_cnt,
DMA_TO_DEVICE);
else
dma_unmap_page(NULL, pkt_info.buf_ptr,
pkt_info.byte_cnt,
DMA_TO_DEVICE);
if (pkt_info.return_info)
dev_kfree_skb_irq(pkt_info.return_info);
}
if (netif_queue_stopped(dev) &&
mp->tx_ring_size - mp->tx_desc_count > MAX_DESCS_PER_SKB)
netif_wake_queue(dev);
}
/* /*
* mv643xx_poll * mv643xx_poll
* *
...@@ -1099,7 +1087,7 @@ static int mv643xx_poll(struct net_device *dev, int *budget) ...@@ -1099,7 +1087,7 @@ static int mv643xx_poll(struct net_device *dev, int *budget)
#ifdef MV643XX_TX_FAST_REFILL #ifdef MV643XX_TX_FAST_REFILL
if (++mp->tx_clean_threshold > 5) { if (++mp->tx_clean_threshold > 5) {
mv643xx_tx(dev); mv643xx_eth_free_completed_tx_descs(dev);
mp->tx_clean_threshold = 0; mp->tx_clean_threshold = 0;
} }
#endif #endif
...@@ -1156,11 +1144,9 @@ static int eth_alloc_tx_desc_index(struct mv643xx_private *mp) ...@@ -1156,11 +1144,9 @@ static int eth_alloc_tx_desc_index(struct mv643xx_private *mp)
{ {
int tx_desc_curr; int tx_desc_curr;
tx_desc_curr = mp->tx_curr_desc_q;
BUG_ON(mp->tx_desc_count >= mp->tx_ring_size); BUG_ON(mp->tx_desc_count >= mp->tx_ring_size);
mp->tx_desc_count++;
tx_desc_curr = mp->tx_curr_desc_q;
mp->tx_curr_desc_q = (tx_desc_curr + 1) % mp->tx_ring_size; mp->tx_curr_desc_q = (tx_desc_curr + 1) % mp->tx_ring_size;
BUG_ON(mp->tx_curr_desc_q == mp->tx_used_desc_q); BUG_ON(mp->tx_curr_desc_q == mp->tx_used_desc_q);
...@@ -1180,7 +1166,6 @@ static void eth_tx_fill_frag_descs(struct mv643xx_private *mp, ...@@ -1180,7 +1166,6 @@ static void eth_tx_fill_frag_descs(struct mv643xx_private *mp,
int frag; int frag;
int tx_index; int tx_index;
struct eth_tx_desc *desc; struct eth_tx_desc *desc;
struct net_device_stats *stats = &mp->stats;
for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) { for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag]; skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag];
...@@ -1205,7 +1190,6 @@ static void eth_tx_fill_frag_descs(struct mv643xx_private *mp, ...@@ -1205,7 +1190,6 @@ static void eth_tx_fill_frag_descs(struct mv643xx_private *mp,
this_frag->page_offset, this_frag->page_offset,
this_frag->size, this_frag->size,
DMA_TO_DEVICE); DMA_TO_DEVICE);
stats->tx_bytes += this_frag->size;
} }
} }
...@@ -1215,21 +1199,21 @@ static void eth_tx_fill_frag_descs(struct mv643xx_private *mp, ...@@ -1215,21 +1199,21 @@ static void eth_tx_fill_frag_descs(struct mv643xx_private *mp,
* Ensure the data for an skb to be transmitted is mapped properly, * Ensure the data for an skb to be transmitted is mapped properly,
* then fill in descriptors in the tx hw queue and start the hardware. * then fill in descriptors in the tx hw queue and start the hardware.
*/ */
static int eth_tx_submit_descs_for_skb(struct mv643xx_private *mp, static void eth_tx_submit_descs_for_skb(struct mv643xx_private *mp,
struct sk_buff *skb) struct sk_buff *skb)
{ {
int tx_index; int tx_index;
struct eth_tx_desc *desc; struct eth_tx_desc *desc;
u32 cmd_sts; u32 cmd_sts;
int length; int length;
int tx_bytes = 0; int nr_frags = skb_shinfo(skb)->nr_frags;
cmd_sts = ETH_TX_FIRST_DESC | ETH_GEN_CRC | ETH_BUFFER_OWNED_BY_DMA; cmd_sts = ETH_TX_FIRST_DESC | ETH_GEN_CRC | ETH_BUFFER_OWNED_BY_DMA;
tx_index = eth_alloc_tx_desc_index(mp); tx_index = eth_alloc_tx_desc_index(mp);
desc = &mp->p_tx_desc_area[tx_index]; desc = &mp->p_tx_desc_area[tx_index];
if (skb_shinfo(skb)->nr_frags) { if (nr_frags) {
eth_tx_fill_frag_descs(mp, skb); eth_tx_fill_frag_descs(mp, skb);
length = skb_headlen(skb); length = skb_headlen(skb);
...@@ -1244,7 +1228,6 @@ static int eth_tx_submit_descs_for_skb(struct mv643xx_private *mp, ...@@ -1244,7 +1228,6 @@ static int eth_tx_submit_descs_for_skb(struct mv643xx_private *mp,
desc->byte_cnt = length; desc->byte_cnt = length;
desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE); desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE);
tx_bytes += length;
if (skb->ip_summed == CHECKSUM_HW) { if (skb->ip_summed == CHECKSUM_HW) {
BUG_ON(skb->protocol != ETH_P_IP); BUG_ON(skb->protocol != ETH_P_IP);
...@@ -1276,9 +1259,9 @@ static int eth_tx_submit_descs_for_skb(struct mv643xx_private *mp, ...@@ -1276,9 +1259,9 @@ static int eth_tx_submit_descs_for_skb(struct mv643xx_private *mp,
/* ensure all descriptors are written before poking hardware */ /* ensure all descriptors are written before poking hardware */
wmb(); wmb();
mv643xx_eth_port_enable_tx(mp->port_num, mp->port_tx_queue_command); mv643xx_eth_port_enable_tx(mp->port_num, ETH_TX_QUEUES_ENABLED);
return tx_bytes; mp->tx_desc_count += nr_frags + 1;
} }
/** /**
...@@ -1306,7 +1289,8 @@ static int mv643xx_eth_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -1306,7 +1289,8 @@ static int mv643xx_eth_start_xmit(struct sk_buff *skb, struct net_device *dev)
spin_lock_irqsave(&mp->lock, flags); spin_lock_irqsave(&mp->lock, flags);
stats->tx_bytes = eth_tx_submit_descs_for_skb(mp, skb); eth_tx_submit_descs_for_skb(mp, skb);
stats->tx_bytes = skb->len;
stats->tx_packets++; stats->tx_packets++;
dev->trans_start = jiffies; dev->trans_start = jiffies;
...@@ -1893,7 +1877,7 @@ static void eth_port_start(struct net_device *dev) ...@@ -1893,7 +1877,7 @@ static void eth_port_start(struct net_device *dev)
MV643XX_ETH_PORT_SDMA_CONFIG_DEFAULT_VALUE); MV643XX_ETH_PORT_SDMA_CONFIG_DEFAULT_VALUE);
/* Enable port Rx. */ /* Enable port Rx. */
mv643xx_eth_port_enable_rx(port_num, mp->port_rx_queue_command); mv643xx_eth_port_enable_rx(port_num, ETH_RX_QUEUES_ENABLED);
/* Disable port bandwidth limits by clearing MTU register */ /* Disable port bandwidth limits by clearing MTU register */
mv_write(MV643XX_ETH_MAXIMUM_TRANSMIT_UNIT(port_num), 0); mv_write(MV643XX_ETH_MAXIMUM_TRANSMIT_UNIT(port_num), 0);
...@@ -2601,79 +2585,6 @@ static void mv643xx_mdio_write(struct net_device *dev, int phy_id, int location, ...@@ -2601,79 +2585,6 @@ static void mv643xx_mdio_write(struct net_device *dev, int phy_id, int location,
eth_port_write_smi_reg(mp->port_num, location, val); eth_port_write_smi_reg(mp->port_num, location, val);
} }
/*
* eth_tx_return_desc - Free all used Tx descriptors
*
* DESCRIPTION:
* This routine returns the transmitted packet information to the caller.
* It uses the 'first' index to support Tx desc return in case a transmit
* of a packet spanned over multiple buffer still in process.
* In case the Tx queue was in "resource error" condition, where there are
* no available Tx resources, the function resets the resource error flag.
*
* INPUT:
* struct mv643xx_private *mp Ethernet Port Control srtuct.
* struct pkt_info *p_pkt_info User packet buffer.
*
* OUTPUT:
* Tx ring 'first' and 'used' indexes are updated.
*
* RETURN:
* ETH_OK on success
* ETH_ERROR otherwise.
*
*/
static ETH_FUNC_RET_STATUS eth_tx_return_desc(struct mv643xx_private *mp,
struct pkt_info *p_pkt_info)
{
int tx_desc_used;
struct eth_tx_desc *p_tx_desc_used;
unsigned int command_status;
unsigned long flags;
int err = ETH_OK;
spin_lock_irqsave(&mp->lock, flags);
BUG_ON(mp->tx_desc_count < 0);
if (mp->tx_desc_count == 0) {
/* no more tx descs in use */
err = ETH_ERROR;
goto out;
}
/* Get the Tx Desc ring indexes */
tx_desc_used = mp->tx_used_desc_q;
p_tx_desc_used = &mp->p_tx_desc_area[tx_desc_used];
BUG_ON(p_tx_desc_used == NULL);
command_status = p_tx_desc_used->cmd_sts;
if (command_status & (ETH_BUFFER_OWNED_BY_DMA)) {
/* Still transmitting... */
err = ETH_ERROR;
goto out;
}
/* Pass the packet information to the caller */
p_pkt_info->cmd_sts = command_status;
p_pkt_info->return_info = mp->tx_skb[tx_desc_used];
p_pkt_info->buf_ptr = p_tx_desc_used->buf_ptr;
p_pkt_info->byte_cnt = p_tx_desc_used->byte_cnt;
mp->tx_skb[tx_desc_used] = NULL;
/* Update the next descriptor to release. */
mp->tx_used_desc_q = (tx_desc_used + 1) % mp->tx_ring_size;
BUG_ON(mp->tx_desc_count == 0);
mp->tx_desc_count--;
out:
spin_unlock_irqrestore(&mp->lock, flags);
return err;
}
/* /*
* eth_port_receive - Get received information from Rx ring. * eth_port_receive - Get received information from Rx ring.
* *
......
...@@ -321,8 +321,6 @@ struct mv643xx_mib_counters { ...@@ -321,8 +321,6 @@ struct mv643xx_mib_counters {
struct mv643xx_private { struct mv643xx_private {
int port_num; /* User Ethernet port number */ int port_num; /* User Ethernet port number */
u32 port_tx_queue_command; /* Port active Tx queues summary*/
u32 port_rx_queue_command; /* Port active Rx queues summary*/
u32 rx_sram_addr; /* Base address of rx sram area */ u32 rx_sram_addr; /* Base address of rx sram area */
u32 rx_sram_size; /* Size of rx sram area */ u32 rx_sram_size; /* Size of rx sram area */
...@@ -411,8 +409,6 @@ static void eth_port_read_smi_reg(unsigned int eth_port_num, ...@@ -411,8 +409,6 @@ static void eth_port_read_smi_reg(unsigned int eth_port_num,
static void eth_clear_mib_counters(unsigned int eth_port_num); static void eth_clear_mib_counters(unsigned int eth_port_num);
/* Port data flow control routines */ /* Port data flow control routines */
static ETH_FUNC_RET_STATUS eth_tx_return_desc(struct mv643xx_private *mp,
struct pkt_info *p_pkt_info);
static ETH_FUNC_RET_STATUS eth_port_receive(struct mv643xx_private *mp, static ETH_FUNC_RET_STATUS eth_port_receive(struct mv643xx_private *mp,
struct pkt_info *p_pkt_info); struct pkt_info *p_pkt_info);
static ETH_FUNC_RET_STATUS eth_rx_return_buff(struct mv643xx_private *mp, static ETH_FUNC_RET_STATUS eth_rx_return_buff(struct mv643xx_private *mp,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment