Commit 6db74704 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by David S. Miller

ftgmac100: Add support for fragmented tx

Add NETIF_F_SG and create multiple TX ring entries for skb fragments.

On reclaim, the skb is only freed on the segment marked as "last".
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e9245539
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
#define RX_BUF_SIZE MAX_PKT_SIZE /* must be smaller than 0x3fff */ #define RX_BUF_SIZE MAX_PKT_SIZE /* must be smaller than 0x3fff */
/* Min number of tx ring entries before stopping queue */ /* Min number of tx ring entries before stopping queue */
#define TX_THRESHOLD (1) #define TX_THRESHOLD (MAX_SKB_FRAGS + 1)
struct ftgmac100_descs { struct ftgmac100_descs {
struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES]; struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES];
...@@ -489,20 +489,30 @@ static void ftgmac100_txdes_set_first_segment(struct ftgmac100_txdes *txdes) ...@@ -489,20 +489,30 @@ static void ftgmac100_txdes_set_first_segment(struct ftgmac100_txdes *txdes)
txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_FTS); txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_FTS);
} }
static inline bool ftgmac100_txdes_get_first_segment(struct ftgmac100_txdes *txdes)
{
return (txdes->txdes0 & cpu_to_le32(FTGMAC100_TXDES0_FTS)) != 0;
}
static void ftgmac100_txdes_set_last_segment(struct ftgmac100_txdes *txdes) static void ftgmac100_txdes_set_last_segment(struct ftgmac100_txdes *txdes)
{ {
txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_LTS); txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_LTS);
} }
static inline bool ftgmac100_txdes_get_last_segment(struct ftgmac100_txdes *txdes)
{
return (txdes->txdes0 & cpu_to_le32(FTGMAC100_TXDES0_LTS)) != 0;
}
static void ftgmac100_txdes_set_buffer_size(struct ftgmac100_txdes *txdes, static void ftgmac100_txdes_set_buffer_size(struct ftgmac100_txdes *txdes,
unsigned int len) unsigned int len)
{ {
txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_TXBUF_SIZE(len)); txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_TXBUF_SIZE(len));
} }
static void ftgmac100_txdes_set_txint(struct ftgmac100_txdes *txdes) static inline unsigned int ftgmac100_txdes_get_buffer_size(struct ftgmac100_txdes *txdes)
{ {
txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_TXIC); return FTGMAC100_TXDES0_TXBUF_SIZE(cpu_to_le32(txdes->txdes0));
} }
static void ftgmac100_txdes_set_tcpcs(struct ftgmac100_txdes *txdes) static void ftgmac100_txdes_set_tcpcs(struct ftgmac100_txdes *txdes)
...@@ -528,7 +538,7 @@ static void ftgmac100_txdes_set_dma_addr(struct ftgmac100_txdes *txdes, ...@@ -528,7 +538,7 @@ static void ftgmac100_txdes_set_dma_addr(struct ftgmac100_txdes *txdes,
static dma_addr_t ftgmac100_txdes_get_dma_addr(struct ftgmac100_txdes *txdes) static dma_addr_t ftgmac100_txdes_get_dma_addr(struct ftgmac100_txdes *txdes)
{ {
return le32_to_cpu(txdes->txdes3); return (dma_addr_t)le32_to_cpu(txdes->txdes3);
} }
static int ftgmac100_next_tx_pointer(int pointer) static int ftgmac100_next_tx_pointer(int pointer)
...@@ -558,13 +568,19 @@ static void ftgmac100_free_tx_packet(struct ftgmac100 *priv, ...@@ -558,13 +568,19 @@ static void ftgmac100_free_tx_packet(struct ftgmac100 *priv,
struct sk_buff *skb, struct sk_buff *skb,
struct ftgmac100_txdes *txdes) struct ftgmac100_txdes *txdes)
{ {
dma_addr_t map; dma_addr_t map = ftgmac100_txdes_get_dma_addr(txdes);
map = ftgmac100_txdes_get_dma_addr(txdes); if (ftgmac100_txdes_get_first_segment(txdes)) {
dma_unmap_single(priv->dev, map, skb_headlen(skb),
dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE); DMA_TO_DEVICE);
} else {
dma_unmap_page(priv->dev, map,
ftgmac100_txdes_get_buffer_size(txdes),
DMA_TO_DEVICE);
}
dev_kfree_skb(skb); if (ftgmac100_txdes_get_last_segment(txdes))
dev_kfree_skb(skb);
priv->tx_skbs[pointer] = NULL; priv->tx_skbs[pointer] = NULL;
/* Clear txdes0 except end of ring bit, clear txdes1 as we /* Clear txdes0 except end of ring bit, clear txdes1 as we
...@@ -626,8 +642,8 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, ...@@ -626,8 +642,8 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
struct net_device *netdev) struct net_device *netdev)
{ {
struct ftgmac100 *priv = netdev_priv(netdev); struct ftgmac100 *priv = netdev_priv(netdev);
struct ftgmac100_txdes *txdes; struct ftgmac100_txdes *txdes, *first;
unsigned int pointer; unsigned int pointer, nfrags, len, i, j;
dma_addr_t map; dma_addr_t map;
/* The HW doesn't pad small frames */ /* The HW doesn't pad small frames */
...@@ -643,26 +659,33 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, ...@@ -643,26 +659,33 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
goto drop; goto drop;
} }
map = dma_map_single(priv->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); /* Do we have a limit on #fragments ? I yet have to get a reply
if (unlikely(dma_mapping_error(priv->dev, map))) { * from Aspeed. If there's one I haven't hit it.
/* drop packet */ */
nfrags = skb_shinfo(skb)->nr_frags;
/* Get header len */
len = skb_headlen(skb);
/* Map the packet head */
map = dma_map_single(priv->dev, skb->data, len, DMA_TO_DEVICE);
if (dma_mapping_error(priv->dev, map)) {
if (net_ratelimit()) if (net_ratelimit())
netdev_err(netdev, "map socket buffer failed\n"); netdev_err(netdev, "map tx packet head failed\n");
goto drop; goto drop;
} }
/* Grab the next free tx descriptor */ /* Grab the next free tx descriptor */
pointer = priv->tx_pointer; pointer = priv->tx_pointer;
txdes = &priv->descs->txdes[pointer]; txdes = first = &priv->descs->txdes[pointer];
/* setup TX descriptor */ /* Setup it up with the packet head. We don't set the OWN bit yet. */
priv->tx_skbs[pointer] = skb; priv->tx_skbs[pointer] = skb;
ftgmac100_txdes_set_dma_addr(txdes, map); ftgmac100_txdes_set_dma_addr(txdes, map);
ftgmac100_txdes_set_buffer_size(txdes, skb->len); ftgmac100_txdes_set_buffer_size(txdes, len);
ftgmac100_txdes_set_first_segment(txdes); ftgmac100_txdes_set_first_segment(txdes);
ftgmac100_txdes_set_last_segment(txdes);
ftgmac100_txdes_set_txint(txdes); /* Setup HW checksumming */
if (skb->ip_summed == CHECKSUM_PARTIAL) { if (skb->ip_summed == CHECKSUM_PARTIAL) {
__be16 protocol = skb->protocol; __be16 protocol = skb->protocol;
...@@ -677,14 +700,41 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, ...@@ -677,14 +700,41 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
} }
} }
/* Next descriptor */
pointer = ftgmac100_next_tx_pointer(pointer);
/* Add the fragments */
for (i = 0; i < nfrags; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
len = frag->size;
/* Map it */
map = skb_frag_dma_map(priv->dev, frag, 0, len,
DMA_TO_DEVICE);
if (dma_mapping_error(priv->dev, map))
goto dma_err;
/* Setup descriptor */
priv->tx_skbs[pointer] = skb;
txdes = &priv->descs->txdes[pointer];
ftgmac100_txdes_set_dma_addr(txdes, map);
ftgmac100_txdes_set_buffer_size(txdes, len);
ftgmac100_txdes_set_dma_own(txdes);
pointer = ftgmac100_next_tx_pointer(pointer);
}
/* Tag last fragment */
ftgmac100_txdes_set_last_segment(txdes);
/* Order the previous packet and descriptor udpates /* Order the previous packet and descriptor udpates
* before setting the OWN bit. * before setting the OWN bit.
*/ */
dma_wmb(); dma_wmb();
ftgmac100_txdes_set_dma_own(txdes); ftgmac100_txdes_set_dma_own(first);
/* Update next TX pointer */ /* Update next TX pointer */
priv->tx_pointer = ftgmac100_next_tx_pointer(pointer); priv->tx_pointer = pointer;
/* If there isn't enough room for all the fragments of a new packet /* If there isn't enough room for all the fragments of a new packet
* in the TX ring, stop the queue. The sequence below is race free * in the TX ring, stop the queue. The sequence below is race free
...@@ -702,6 +752,25 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, ...@@ -702,6 +752,25 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
return NETDEV_TX_OK; return NETDEV_TX_OK;
dma_err:
if (net_ratelimit())
netdev_err(netdev, "map tx fragment failed\n");
/* Free head */
pointer = priv->tx_pointer;
ftgmac100_free_tx_packet(priv, pointer, skb, first);
/* Then all fragments */
for (j = 0; j < i; j++) {
pointer = ftgmac100_next_tx_pointer(pointer);
txdes = &priv->descs->txdes[pointer];
ftgmac100_free_tx_packet(priv, pointer, skb, txdes);
}
/* This cannot be reached if we successfully mapped the
* last fragment, so we know ftgmac100_free_tx_packet()
* hasn't freed the skb yet.
*/
drop: drop:
/* Drop the packet */ /* Drop the packet */
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
...@@ -1441,12 +1510,12 @@ static int ftgmac100_probe(struct platform_device *pdev) ...@@ -1441,12 +1510,12 @@ static int ftgmac100_probe(struct platform_device *pdev)
* when NCSI is enabled on the interface. It doesn't work * when NCSI is enabled on the interface. It doesn't work
* in that case. * in that case.
*/ */
netdev->features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_GRO; netdev->features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM |
NETIF_F_GRO | NETIF_F_SG;
if (priv->use_ncsi && if (priv->use_ncsi &&
of_get_property(pdev->dev.of_node, "no-hw-checksum", NULL)) of_get_property(pdev->dev.of_node, "no-hw-checksum", NULL))
netdev->features &= ~NETIF_F_IP_CSUM; netdev->features &= ~NETIF_F_IP_CSUM;
/* register network device */ /* register network device */
err = register_netdev(netdev); err = register_netdev(netdev);
if (err) { if (err) {
......
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