Commit 48544cc2 authored by Masakazu Mokuno's avatar Masakazu Mokuno Committed by Jeff Garzik

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: default avatarMasakazu Mokuno <mokuno@sm.sony.co.jp>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent ea6992aa
...@@ -408,22 +408,25 @@ static void gelic_net_release_tx_chain(struct gelic_net_card *card, int stop) ...@@ -408,22 +408,25 @@ static void gelic_net_release_tx_chain(struct gelic_net_card *card, int stop)
break; break;
case GELIC_NET_DESCR_COMPLETE: case GELIC_NET_DESCR_COMPLETE:
card->netdev_stats.tx_packets++; if (tx_chain->tail->skb) {
card->netdev_stats.tx_bytes += card->netdev_stats.tx_packets++;
tx_chain->tail->skb->len; card->netdev_stats.tx_bytes +=
tx_chain->tail->skb->len;
}
break; break;
case GELIC_NET_DESCR_CARDOWNED: case GELIC_NET_DESCR_CARDOWNED:
/* pending tx request */ /* pending tx request */
default: default:
/* any other value (== GELIC_NET_DESCR_NOT_IN_USE) */ /* any other value (== GELIC_NET_DESCR_NOT_IN_USE) */
goto out; if (!stop)
goto out;
} }
gelic_net_release_tx_descr(card, tx_chain->tail); gelic_net_release_tx_descr(card, tx_chain->tail);
release = 1; release ++;
} }
out: out:
if (!stop && release) if (!stop && (2 < release))
netif_wake_queue(card->netdev); netif_wake_queue(card->netdev);
} }
...@@ -660,19 +663,21 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card, ...@@ -660,19 +663,21 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card,
{ {
dma_addr_t buf[2]; dma_addr_t buf[2];
unsigned int vlan_len; unsigned int vlan_len;
struct gelic_net_descr *sec_descr = descr->next;
if (skb->len < GELIC_NET_VLAN_POS) if (skb->len < GELIC_NET_VLAN_POS)
return -EINVAL; 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) { if (card->vlan_index != -1) {
/* internal vlan tag used */
descr->vlan.h_vlan_proto = htons(ETH_P_8021Q); /* vlan 0x8100*/ descr->vlan.h_vlan_proto = htons(ETH_P_8021Q); /* vlan 0x8100*/
descr->vlan.h_vlan_TCI = htons(card->vlan_id[card->vlan_index]); descr->vlan.h_vlan_TCI = htons(card->vlan_id[card->vlan_index]);
vlan_len = GELIC_NET_VLAN_POS + VLAN_HLEN; /* VLAN_HLEN=4 */ vlan_len += VLAN_HLEN; /* added for above two lines */
} else }
vlan_len = GELIC_NET_VLAN_POS; /* no vlan tag */
/* first descr */ /* map data area */
buf[0] = dma_map_single(ctodev(card), &descr->vlan, buf[0] = dma_map_single(ctodev(card), &descr->vlan,
vlan_len, DMA_TO_DEVICE); vlan_len, DMA_TO_DEVICE);
...@@ -683,20 +688,6 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card, ...@@ -683,20 +688,6 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card,
return -ENOMEM; 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, buf[1] = dma_map_single(ctodev(card), skb->data + GELIC_NET_VLAN_POS,
skb->len - GELIC_NET_VLAN_POS, skb->len - GELIC_NET_VLAN_POS,
DMA_TO_DEVICE); DMA_TO_DEVICE);
...@@ -711,13 +702,24 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card, ...@@ -711,13 +702,24 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card,
return -ENOMEM; return -ENOMEM;
} }
descr->buf_addr = buf[1]; /* first descr */
descr->buf_size = skb->len - GELIC_NET_VLAN_POS; descr->buf_addr = buf[0];
descr->skb = skb; descr->buf_size = vlan_len;
descr->skb = NULL; /* not used */
descr->data_status = 0; descr->data_status = 0;
descr->next_descr_addr = 0; /* terminate hw descr */ descr->next_descr_addr = descr->next->bus_addr;
gelic_net_set_txdescr_cmdstat(descr, skb, 0); 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; return 0;
} }
...@@ -730,7 +732,7 @@ static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card, ...@@ -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, static int gelic_net_kick_txdma(struct gelic_net_card *card,
struct gelic_net_descr *descr) struct gelic_net_descr *descr)
{ {
int status = -ENXIO; int status = 0;
int count = 10; int count = 10;
if (card->tx_dma_progress) if (card->tx_dma_progress)
...@@ -764,47 +766,62 @@ static int gelic_net_kick_txdma(struct gelic_net_card *card, ...@@ -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) static int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev)
{ {
struct gelic_net_card *card = netdev_priv(netdev); struct gelic_net_card *card = netdev_priv(netdev);
struct gelic_net_descr *descr = NULL; struct gelic_net_descr *descr;
int result; int result;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&card->tx_dma_lock, flags); spin_lock_irqsave(&card->tx_dma_lock, flags);
gelic_net_release_tx_chain(card, 0); gelic_net_release_tx_chain(card, 0);
if (!skb)
goto kick;
descr = gelic_net_get_next_tx_descr(card); descr = gelic_net_get_next_tx_descr(card);
if (!descr) { if (!descr) {
/*
* no more descriptors free
*/
netif_stop_queue(netdev); netif_stop_queue(netdev);
spin_unlock_irqrestore(&card->tx_dma_lock, flags); spin_unlock_irqrestore(&card->tx_dma_lock, flags);
return NETDEV_TX_BUSY; 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) result = gelic_net_prepare_tx_descr_v(card, descr, skb);
descr->prev->next_descr_addr = descr->bus_addr; if (result) {
kick: /*
* 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, * as hardware descriptor is modified in the above lines,
* ensure that the hardware sees it * ensure that the hardware sees it
*/ */
wmb(); wmb();
if (gelic_net_kick_txdma(card, card->tx_chain.tail)) if (gelic_net_kick_txdma(card, descr)) {
goto error; /*
* 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); spin_unlock_irqrestore(&card->tx_dma_lock, flags);
return NETDEV_TX_OK; 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) ...@@ -1025,9 +1042,10 @@ static irqreturn_t gelic_net_interrupt(int irq, void *ptr)
if (status & GELIC_NET_TXINT) { if (status & GELIC_NET_TXINT) {
spin_lock_irqsave(&card->tx_dma_lock, flags); spin_lock_irqsave(&card->tx_dma_lock, flags);
card->tx_dma_progress = 0; 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); spin_unlock_irqrestore(&card->tx_dma_lock, flags);
/* start pending DMA */
gelic_net_xmit(NULL, netdev);
} }
return IRQ_HANDLED; return IRQ_HANDLED;
} }
......
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