Commit 50f238fd authored by Anton Vorontsov's avatar Anton Vorontsov Committed by David S. Miller

ucc_geth: Add support for skb recycling

We can reclaim transmitted skbs to use in the receive path, so-called
skb recycling support.

Also reorder ucc_geth_poll() steps, so that we'll clean tx ring firstly,
thus maybe reclaim some skbs for rx.
Signed-off-by: default avatarAnton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ef0657c4
...@@ -209,9 +209,10 @@ static struct sk_buff *get_new_skb(struct ucc_geth_private *ugeth, ...@@ -209,9 +209,10 @@ static struct sk_buff *get_new_skb(struct ucc_geth_private *ugeth,
{ {
struct sk_buff *skb = NULL; struct sk_buff *skb = NULL;
skb = dev_alloc_skb(ugeth->ug_info->uf_info.max_rx_buf_length + skb = __skb_dequeue(&ugeth->rx_recycle);
UCC_GETH_RX_DATA_BUF_ALIGNMENT); if (!skb)
skb = dev_alloc_skb(ugeth->ug_info->uf_info.max_rx_buf_length +
UCC_GETH_RX_DATA_BUF_ALIGNMENT);
if (skb == NULL) if (skb == NULL)
return NULL; return NULL;
...@@ -1986,6 +1987,8 @@ static void ucc_geth_memclean(struct ucc_geth_private *ugeth) ...@@ -1986,6 +1987,8 @@ static void ucc_geth_memclean(struct ucc_geth_private *ugeth)
iounmap(ugeth->ug_regs); iounmap(ugeth->ug_regs);
ugeth->ug_regs = NULL; ugeth->ug_regs = NULL;
} }
skb_queue_purge(&ugeth->rx_recycle);
} }
static void ucc_geth_set_multi(struct net_device *dev) static void ucc_geth_set_multi(struct net_device *dev)
...@@ -2202,6 +2205,8 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth) ...@@ -2202,6 +2205,8 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
return -ENOMEM; return -ENOMEM;
} }
skb_queue_head_init(&ugeth->rx_recycle);
return 0; return 0;
} }
...@@ -3208,8 +3213,10 @@ static int ucc_geth_rx(struct ucc_geth_private *ugeth, u8 rxQ, int rx_work_limit ...@@ -3208,8 +3213,10 @@ static int ucc_geth_rx(struct ucc_geth_private *ugeth, u8 rxQ, int rx_work_limit
if (netif_msg_rx_err(ugeth)) if (netif_msg_rx_err(ugeth))
ugeth_err("%s, %d: ERROR!!! skb - 0x%08x", ugeth_err("%s, %d: ERROR!!! skb - 0x%08x",
__func__, __LINE__, (u32) skb); __func__, __LINE__, (u32) skb);
if (skb) if (skb) {
dev_kfree_skb_any(skb); skb->data = skb->head + NET_SKB_PAD;
__skb_queue_head(&ugeth->rx_recycle, skb);
}
ugeth->rx_skbuff[rxQ][ugeth->skb_currx[rxQ]] = NULL; ugeth->rx_skbuff[rxQ][ugeth->skb_currx[rxQ]] = NULL;
dev->stats.rx_dropped++; dev->stats.rx_dropped++;
...@@ -3267,6 +3274,8 @@ static int ucc_geth_tx(struct net_device *dev, u8 txQ) ...@@ -3267,6 +3274,8 @@ static int ucc_geth_tx(struct net_device *dev, u8 txQ)
/* Normal processing. */ /* Normal processing. */
while ((bd_status & T_R) == 0) { while ((bd_status & T_R) == 0) {
struct sk_buff *skb;
/* BD contains already transmitted buffer. */ /* BD contains already transmitted buffer. */
/* Handle the transmitted buffer and release */ /* Handle the transmitted buffer and release */
/* the BD to be used with the current frame */ /* the BD to be used with the current frame */
...@@ -3276,9 +3285,16 @@ static int ucc_geth_tx(struct net_device *dev, u8 txQ) ...@@ -3276,9 +3285,16 @@ static int ucc_geth_tx(struct net_device *dev, u8 txQ)
dev->stats.tx_packets++; dev->stats.tx_packets++;
/* Free the sk buffer associated with this TxBD */ skb = ugeth->tx_skbuff[txQ][ugeth->skb_dirtytx[txQ]];
dev_kfree_skb(ugeth->
tx_skbuff[txQ][ugeth->skb_dirtytx[txQ]]); if (skb_queue_len(&ugeth->rx_recycle) < RX_BD_RING_LEN &&
skb_recycle_check(skb,
ugeth->ug_info->uf_info.max_rx_buf_length +
UCC_GETH_RX_DATA_BUF_ALIGNMENT))
__skb_queue_head(&ugeth->rx_recycle, skb);
else
dev_kfree_skb(skb);
ugeth->tx_skbuff[txQ][ugeth->skb_dirtytx[txQ]] = NULL; ugeth->tx_skbuff[txQ][ugeth->skb_dirtytx[txQ]] = NULL;
ugeth->skb_dirtytx[txQ] = ugeth->skb_dirtytx[txQ] =
(ugeth->skb_dirtytx[txQ] + (ugeth->skb_dirtytx[txQ] +
...@@ -3307,16 +3323,16 @@ static int ucc_geth_poll(struct napi_struct *napi, int budget) ...@@ -3307,16 +3323,16 @@ static int ucc_geth_poll(struct napi_struct *napi, int budget)
ug_info = ugeth->ug_info; ug_info = ugeth->ug_info;
howmany = 0;
for (i = 0; i < ug_info->numQueuesRx; i++)
howmany += ucc_geth_rx(ugeth, i, budget - howmany);
/* Tx event processing */ /* Tx event processing */
spin_lock(&ugeth->lock); spin_lock(&ugeth->lock);
for (i = 0; i < ug_info->numQueuesTx; i++) for (i = 0; i < ug_info->numQueuesTx; i++)
ucc_geth_tx(ugeth->ndev, i); ucc_geth_tx(ugeth->ndev, i);
spin_unlock(&ugeth->lock); spin_unlock(&ugeth->lock);
howmany = 0;
for (i = 0; i < ug_info->numQueuesRx; i++)
howmany += ucc_geth_rx(ugeth, i, budget - howmany);
if (howmany < budget) { if (howmany < budget) {
napi_complete(napi); napi_complete(napi);
setbits32(ugeth->uccf->p_uccm, UCCE_RX_EVENTS | UCCE_TX_EVENTS); setbits32(ugeth->uccf->p_uccm, UCCE_RX_EVENTS | UCCE_TX_EVENTS);
......
...@@ -1212,6 +1212,8 @@ struct ucc_geth_private { ...@@ -1212,6 +1212,8 @@ struct ucc_geth_private {
/* index of the first skb which hasn't been transmitted yet. */ /* index of the first skb which hasn't been transmitted yet. */
u16 skb_dirtytx[NUM_TX_QUEUES]; u16 skb_dirtytx[NUM_TX_QUEUES];
struct sk_buff_head rx_recycle;
struct ugeth_mii_info *mii_info; struct ugeth_mii_info *mii_info;
struct phy_device *phydev; struct phy_device *phydev;
phy_interface_t phy_interface; phy_interface_t phy_interface;
......
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