ps3: tx descriptor handling cleanup

gelic: TX descriptor handling cleanup

        - Emitted return value of NETDEV_TX_LOCKED when DMA map or kick
          failure.
          Now it would free the skb, update drop packet statistics
          and return OK. Requested from Jeff Garzik.
        - Enable tx queue if number of free descriptors are more than 2
        - Fixed descriptor leak if dma map for second descriptor failed
        - Stopped calling xmit handler from interrupt handler in order
          to recheck tx queue.  Instead, call appropriate helper functions.

Signed-off-by: Masakazu Mokuno <mokuno@sm.sony.co.jp>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
This commit is contained in:
Masakazu Mokuno 2007-07-20 17:24:56 +09:00 committed by Jeff Garzik
parent ea6992aa1f
commit 48544cc267

View file

@ -408,22 +408,25 @@ static void gelic_net_release_tx_chain(struct gelic_net_card *card, int stop)
break;
case GELIC_NET_DESCR_COMPLETE:
card->netdev_stats.tx_packets++;
card->netdev_stats.tx_bytes +=
tx_chain->tail->skb->len;
if (tx_chain->tail->skb) {
card->netdev_stats.tx_packets++;
card->netdev_stats.tx_bytes +=
tx_chain->tail->skb->len;
}
break;
case GELIC_NET_DESCR_CARDOWNED:
/* pending tx request */
default:
/* any other value (== GELIC_NET_DESCR_NOT_IN_USE) */
goto out;
if (!stop)
goto out;
}
gelic_net_release_tx_descr(card, tx_chain->tail);
release = 1;
release ++;
}
out:
if (!stop && release)
if (!stop && (2 < release))
netif_wake_queue(card->netdev);
}
@ -660,19 +663,21 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card,
{
dma_addr_t buf[2];
unsigned int vlan_len;
struct gelic_net_descr *sec_descr = descr->next;
if (skb->len < GELIC_NET_VLAN_POS)
return -EINVAL;
memcpy(&descr->vlan, skb->data, GELIC_NET_VLAN_POS);
vlan_len = GELIC_NET_VLAN_POS;
memcpy(&descr->vlan, skb->data, vlan_len);
if (card->vlan_index != -1) {
/* internal vlan tag used */
descr->vlan.h_vlan_proto = htons(ETH_P_8021Q); /* vlan 0x8100*/
descr->vlan.h_vlan_TCI = htons(card->vlan_id[card->vlan_index]);
vlan_len = GELIC_NET_VLAN_POS + VLAN_HLEN; /* VLAN_HLEN=4 */
} else
vlan_len = GELIC_NET_VLAN_POS; /* no vlan tag */
vlan_len += VLAN_HLEN; /* added for above two lines */
}
/* first descr */
/* map data area */
buf[0] = dma_map_single(ctodev(card), &descr->vlan,
vlan_len, DMA_TO_DEVICE);
@ -683,20 +688,6 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card,
return -ENOMEM;
}
descr->buf_addr = buf[0];
descr->buf_size = vlan_len;
descr->skb = skb; /* not used */
descr->data_status = 0;
gelic_net_set_txdescr_cmdstat(descr, skb, 1); /* not the frame end */
/* second descr */
card->tx_chain.head = card->tx_chain.head->next;
descr->next_descr_addr = descr->next->bus_addr;
descr = descr->next;
if (gelic_net_get_descr_status(descr) != GELIC_NET_DESCR_NOT_IN_USE)
/* XXX will be removed */
dev_err(ctodev(card), "descr is not free!\n");
buf[1] = dma_map_single(ctodev(card), skb->data + GELIC_NET_VLAN_POS,
skb->len - GELIC_NET_VLAN_POS,
DMA_TO_DEVICE);
@ -711,13 +702,24 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card,
return -ENOMEM;
}
descr->buf_addr = buf[1];
descr->buf_size = skb->len - GELIC_NET_VLAN_POS;
descr->skb = skb;
/* first descr */
descr->buf_addr = buf[0];
descr->buf_size = vlan_len;
descr->skb = NULL; /* not used */
descr->data_status = 0;
descr->next_descr_addr = 0; /* terminate hw descr */
gelic_net_set_txdescr_cmdstat(descr, skb, 0);
descr->next_descr_addr = descr->next->bus_addr;
gelic_net_set_txdescr_cmdstat(descr, skb, 1); /* not the frame end */
/* second descr */
sec_descr->buf_addr = buf[1];
sec_descr->buf_size = skb->len - GELIC_NET_VLAN_POS;
sec_descr->skb = skb;
sec_descr->data_status = 0;
sec_descr->next_descr_addr = 0; /* terminate hw descr */
gelic_net_set_txdescr_cmdstat(sec_descr, skb, 0);
/* bump free descriptor pointer */
card->tx_chain.head = sec_descr->next;
return 0;
}
@ -730,7 +732,7 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card,
static int gelic_net_kick_txdma(struct gelic_net_card *card,
struct gelic_net_descr *descr)
{
int status = -ENXIO;
int status = 0;
int count = 10;
if (card->tx_dma_progress)
@ -764,47 +766,62 @@ static int gelic_net_kick_txdma(struct gelic_net_card *card,
static int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct gelic_net_card *card = netdev_priv(netdev);
struct gelic_net_descr *descr = NULL;
struct gelic_net_descr *descr;
int result;
unsigned long flags;
spin_lock_irqsave(&card->tx_dma_lock, flags);
gelic_net_release_tx_chain(card, 0);
if (!skb)
goto kick;
descr = gelic_net_get_next_tx_descr(card);
if (!descr) {
/*
* no more descriptors free
*/
netif_stop_queue(netdev);
spin_unlock_irqrestore(&card->tx_dma_lock, flags);
return NETDEV_TX_BUSY;
}
result = gelic_net_prepare_tx_descr_v(card, descr, skb);
if (result)
goto error;
card->tx_chain.head = card->tx_chain.head->next;
if (descr->prev)
descr->prev->next_descr_addr = descr->bus_addr;
kick:
if (result) {
/*
* DMA map failed. As chanses are that failure
* would continue, just release skb and return
*/
card->netdev_stats.tx_dropped++;
dev_kfree_skb_any(skb);
spin_unlock_irqrestore(&card->tx_dma_lock, flags);
return NETDEV_TX_OK;
}
/*
* link this prepared descriptor to previous one
* to achieve high performance
*/
descr->prev->next_descr_addr = descr->bus_addr;
/*
* as hardware descriptor is modified in the above lines,
* ensure that the hardware sees it
*/
wmb();
if (gelic_net_kick_txdma(card, card->tx_chain.tail))
goto error;
if (gelic_net_kick_txdma(card, descr)) {
/*
* kick failed.
* release descriptors which were just prepared
*/
card->netdev_stats.tx_dropped++;
gelic_net_release_tx_descr(card, descr);
gelic_net_release_tx_descr(card, descr->next);
card->tx_chain.tail = descr->next->next;
dev_info(ctodev(card), "%s: kick failure\n", __func__);
} else {
/* OK, DMA started/reserved */
netdev->trans_start = jiffies;
}
netdev->trans_start = jiffies;
spin_unlock_irqrestore(&card->tx_dma_lock, flags);
return NETDEV_TX_OK;
error:
card->netdev_stats.tx_dropped++;
spin_unlock_irqrestore(&card->tx_dma_lock, flags);
return NETDEV_TX_LOCKED;
}
/**
@ -1025,9 +1042,10 @@ static irqreturn_t gelic_net_interrupt(int irq, void *ptr)
if (status & GELIC_NET_TXINT) {
spin_lock_irqsave(&card->tx_dma_lock, flags);
card->tx_dma_progress = 0;
gelic_net_release_tx_chain(card, 0);
/* kick outstanding tx descriptor if any */
gelic_net_kick_txdma(card, card->tx_chain.tail);
spin_unlock_irqrestore(&card->tx_dma_lock, flags);
/* start pending DMA */
gelic_net_xmit(NULL, netdev);
}
return IRQ_HANDLED;
}