net: fs_enet: Implement NETIF_F_SG feature
Freescale ethernet controllers have the capability to re-assemble fragmented data into a single ethernet frame. This patch uses this capability and implements NETIP_F_SG feature into the fs_enet ethernet driver. On a MPC885, I get 53% performance improvement on a ftp transfer of a 15Mb file: * Without the patch : 2,8 Mbps * With the patch : 4,3 Mbps Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
2bd82484bb
commit
4fc9b87bae
|
@ -278,14 +278,20 @@ static int fs_enet_tx_napi(struct napi_struct *napi, int budget)
|
||||||
fep->stats.collisions++;
|
fep->stats.collisions++;
|
||||||
|
|
||||||
/* unmap */
|
/* unmap */
|
||||||
dma_unmap_single(fep->dev, CBDR_BUFADDR(bdp),
|
if (fep->mapped_as_page[dirtyidx])
|
||||||
skb->len, DMA_TO_DEVICE);
|
dma_unmap_page(fep->dev, CBDR_BUFADDR(bdp),
|
||||||
|
CBDR_DATLEN(bdp), DMA_TO_DEVICE);
|
||||||
|
else
|
||||||
|
dma_unmap_single(fep->dev, CBDR_BUFADDR(bdp),
|
||||||
|
CBDR_DATLEN(bdp), DMA_TO_DEVICE);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Free the sk buffer associated with this last transmit.
|
* Free the sk buffer associated with this last transmit.
|
||||||
*/
|
*/
|
||||||
dev_kfree_skb(skb);
|
if (skb) {
|
||||||
fep->tx_skbuff[dirtyidx] = NULL;
|
dev_kfree_skb(skb);
|
||||||
|
fep->tx_skbuff[dirtyidx] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update pointer to next buffer descriptor to be transmitted.
|
* Update pointer to next buffer descriptor to be transmitted.
|
||||||
|
@ -299,7 +305,7 @@ static int fs_enet_tx_napi(struct napi_struct *napi, int budget)
|
||||||
* Since we have freed up a buffer, the ring is no longer
|
* Since we have freed up a buffer, the ring is no longer
|
||||||
* full.
|
* full.
|
||||||
*/
|
*/
|
||||||
if (!fep->tx_free++)
|
if (++fep->tx_free >= MAX_SKB_FRAGS)
|
||||||
do_wake = 1;
|
do_wake = 1;
|
||||||
has_tx_work = 1;
|
has_tx_work = 1;
|
||||||
}
|
}
|
||||||
|
@ -509,6 +515,9 @@ static int fs_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
cbd_t __iomem *bdp;
|
cbd_t __iomem *bdp;
|
||||||
int curidx;
|
int curidx;
|
||||||
u16 sc;
|
u16 sc;
|
||||||
|
int nr_frags = skb_shinfo(skb)->nr_frags;
|
||||||
|
skb_frag_t *frag;
|
||||||
|
int len;
|
||||||
|
|
||||||
#ifdef CONFIG_FS_ENET_MPC5121_FEC
|
#ifdef CONFIG_FS_ENET_MPC5121_FEC
|
||||||
if (((unsigned long)skb->data) & 0x3) {
|
if (((unsigned long)skb->data) & 0x3) {
|
||||||
|
@ -530,7 +539,7 @@ static int fs_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
*/
|
*/
|
||||||
bdp = fep->cur_tx;
|
bdp = fep->cur_tx;
|
||||||
|
|
||||||
if (!fep->tx_free || (CBDR_SC(bdp) & BD_ENET_TX_READY)) {
|
if (fep->tx_free <= nr_frags || (CBDR_SC(bdp) & BD_ENET_TX_READY)) {
|
||||||
netif_stop_queue(dev);
|
netif_stop_queue(dev);
|
||||||
spin_unlock(&fep->tx_lock);
|
spin_unlock(&fep->tx_lock);
|
||||||
|
|
||||||
|
@ -543,35 +552,42 @@ static int fs_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
curidx = bdp - fep->tx_bd_base;
|
curidx = bdp - fep->tx_bd_base;
|
||||||
/*
|
|
||||||
* Clear all of the status flags.
|
|
||||||
*/
|
|
||||||
CBDC_SC(bdp, BD_ENET_TX_STATS);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Save skb pointer.
|
|
||||||
*/
|
|
||||||
fep->tx_skbuff[curidx] = skb;
|
|
||||||
|
|
||||||
fep->stats.tx_bytes += skb->len;
|
|
||||||
|
|
||||||
|
len = skb->len;
|
||||||
|
fep->stats.tx_bytes += len;
|
||||||
|
if (nr_frags)
|
||||||
|
len -= skb->data_len;
|
||||||
|
fep->tx_free -= nr_frags + 1;
|
||||||
/*
|
/*
|
||||||
* Push the data cache so the CPM does not get stale memory data.
|
* Push the data cache so the CPM does not get stale memory data.
|
||||||
*/
|
*/
|
||||||
CBDW_BUFADDR(bdp, dma_map_single(fep->dev,
|
CBDW_BUFADDR(bdp, dma_map_single(fep->dev,
|
||||||
skb->data, skb->len, DMA_TO_DEVICE));
|
skb->data, len, DMA_TO_DEVICE));
|
||||||
CBDW_DATLEN(bdp, skb->len);
|
CBDW_DATLEN(bdp, len);
|
||||||
|
|
||||||
/*
|
fep->mapped_as_page[curidx] = 0;
|
||||||
* If this was the last BD in the ring, start at the beginning again.
|
frag = skb_shinfo(skb)->frags;
|
||||||
*/
|
while (nr_frags) {
|
||||||
if ((CBDR_SC(bdp) & BD_ENET_TX_WRAP) == 0)
|
CBDC_SC(bdp,
|
||||||
fep->cur_tx++;
|
BD_ENET_TX_STATS | BD_ENET_TX_LAST | BD_ENET_TX_TC);
|
||||||
else
|
CBDS_SC(bdp, BD_ENET_TX_READY);
|
||||||
fep->cur_tx = fep->tx_bd_base;
|
|
||||||
|
|
||||||
if (!--fep->tx_free)
|
if ((CBDR_SC(bdp) & BD_ENET_TX_WRAP) == 0)
|
||||||
netif_stop_queue(dev);
|
bdp++, curidx++;
|
||||||
|
else
|
||||||
|
bdp = fep->tx_bd_base, curidx = 0;
|
||||||
|
|
||||||
|
len = skb_frag_size(frag);
|
||||||
|
CBDW_BUFADDR(bdp, skb_frag_dma_map(fep->dev, frag, 0, len,
|
||||||
|
DMA_TO_DEVICE));
|
||||||
|
CBDW_DATLEN(bdp, len);
|
||||||
|
|
||||||
|
fep->tx_skbuff[curidx] = NULL;
|
||||||
|
fep->mapped_as_page[curidx] = 1;
|
||||||
|
|
||||||
|
frag++;
|
||||||
|
nr_frags--;
|
||||||
|
}
|
||||||
|
|
||||||
/* Trigger transmission start */
|
/* Trigger transmission start */
|
||||||
sc = BD_ENET_TX_READY | BD_ENET_TX_INTR |
|
sc = BD_ENET_TX_READY | BD_ENET_TX_INTR |
|
||||||
|
@ -582,8 +598,22 @@ static int fs_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
* yay for hw reuse :) */
|
* yay for hw reuse :) */
|
||||||
if (skb->len <= 60)
|
if (skb->len <= 60)
|
||||||
sc |= BD_ENET_TX_PAD;
|
sc |= BD_ENET_TX_PAD;
|
||||||
|
CBDC_SC(bdp, BD_ENET_TX_STATS);
|
||||||
CBDS_SC(bdp, sc);
|
CBDS_SC(bdp, sc);
|
||||||
|
|
||||||
|
/* Save skb pointer. */
|
||||||
|
fep->tx_skbuff[curidx] = skb;
|
||||||
|
|
||||||
|
/* If this was the last BD in the ring, start at the beginning again. */
|
||||||
|
if ((CBDR_SC(bdp) & BD_ENET_TX_WRAP) == 0)
|
||||||
|
bdp++;
|
||||||
|
else
|
||||||
|
bdp = fep->tx_bd_base;
|
||||||
|
fep->cur_tx = bdp;
|
||||||
|
|
||||||
|
if (fep->tx_free < MAX_SKB_FRAGS)
|
||||||
|
netif_stop_queue(dev);
|
||||||
|
|
||||||
skb_tx_timestamp(skb);
|
skb_tx_timestamp(skb);
|
||||||
|
|
||||||
(*fep->ops->tx_kickstart)(dev);
|
(*fep->ops->tx_kickstart)(dev);
|
||||||
|
@ -917,7 +947,7 @@ static int fs_enet_probe(struct platform_device *ofdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
fpi->rx_ring = 32;
|
fpi->rx_ring = 32;
|
||||||
fpi->tx_ring = 32;
|
fpi->tx_ring = 64;
|
||||||
fpi->rx_copybreak = 240;
|
fpi->rx_copybreak = 240;
|
||||||
fpi->napi_weight = 17;
|
fpi->napi_weight = 17;
|
||||||
fpi->phy_node = of_parse_phandle(ofdev->dev.of_node, "phy-handle", 0);
|
fpi->phy_node = of_parse_phandle(ofdev->dev.of_node, "phy-handle", 0);
|
||||||
|
@ -955,7 +985,8 @@ static int fs_enet_probe(struct platform_device *ofdev)
|
||||||
|
|
||||||
privsize = sizeof(*fep) +
|
privsize = sizeof(*fep) +
|
||||||
sizeof(struct sk_buff **) *
|
sizeof(struct sk_buff **) *
|
||||||
(fpi->rx_ring + fpi->tx_ring);
|
(fpi->rx_ring + fpi->tx_ring) +
|
||||||
|
sizeof(char) * fpi->tx_ring;
|
||||||
|
|
||||||
ndev = alloc_etherdev(privsize);
|
ndev = alloc_etherdev(privsize);
|
||||||
if (!ndev) {
|
if (!ndev) {
|
||||||
|
@ -978,6 +1009,8 @@ static int fs_enet_probe(struct platform_device *ofdev)
|
||||||
|
|
||||||
fep->rx_skbuff = (struct sk_buff **)&fep[1];
|
fep->rx_skbuff = (struct sk_buff **)&fep[1];
|
||||||
fep->tx_skbuff = fep->rx_skbuff + fpi->rx_ring;
|
fep->tx_skbuff = fep->rx_skbuff + fpi->rx_ring;
|
||||||
|
fep->mapped_as_page = (char *)(fep->rx_skbuff + fpi->rx_ring +
|
||||||
|
fpi->tx_ring);
|
||||||
|
|
||||||
spin_lock_init(&fep->lock);
|
spin_lock_init(&fep->lock);
|
||||||
spin_lock_init(&fep->tx_lock);
|
spin_lock_init(&fep->tx_lock);
|
||||||
|
@ -1007,6 +1040,8 @@ static int fs_enet_probe(struct platform_device *ofdev)
|
||||||
|
|
||||||
netif_carrier_off(ndev);
|
netif_carrier_off(ndev);
|
||||||
|
|
||||||
|
ndev->features |= NETIF_F_SG;
|
||||||
|
|
||||||
ret = register_netdev(ndev);
|
ret = register_netdev(ndev);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_free_bd;
|
goto out_free_bd;
|
||||||
|
|
|
@ -134,6 +134,7 @@ struct fs_enet_private {
|
||||||
void __iomem *ring_base;
|
void __iomem *ring_base;
|
||||||
struct sk_buff **rx_skbuff;
|
struct sk_buff **rx_skbuff;
|
||||||
struct sk_buff **tx_skbuff;
|
struct sk_buff **tx_skbuff;
|
||||||
|
char *mapped_as_page;
|
||||||
cbd_t __iomem *rx_bd_base; /* Address of Rx and Tx buffers. */
|
cbd_t __iomem *rx_bd_base; /* Address of Rx and Tx buffers. */
|
||||||
cbd_t __iomem *tx_bd_base;
|
cbd_t __iomem *tx_bd_base;
|
||||||
cbd_t __iomem *dirty_tx; /* ring entries to be free()ed. */
|
cbd_t __iomem *dirty_tx; /* ring entries to be free()ed. */
|
||||||
|
|
Loading…
Reference in New Issue