[PATCH] gainfar.c : skb_over_panic (kernel-2.6.32.15)
From: Eran Liberty
Date: Tue Jun 29 2010 - 11:40:09 EST
David Miller wrote:
From: Eran Liberty <liberty@xxxxxxxxxxxx>
Date: Mon, 28 Jun 2010 10:57:08 +0300
This code has proved to be insufficient and produce
skb_over_panic. The proposed patch fix this.
Then you have to post a patch relative to the current code, rather than
against the code as it was several releases ago.
Your patch didn't apply, so I can't use it.
Upon cleaning up my patch for the latest kernel I realized I do not
like: the previous partial fix, the fix in ucc_geth.c, the fix in the
current latest kernel, and my own previously proposed patch. They all
tried to undo the alignment skb_reserve done in gfar_new_skb() before
queuing the skb into the rw_recycle, because upon getting a new one in
gfar_new_skb() if the skb is from the rx_recycle pool rather then newly
allocated it is reserved twice.
Instead of trying to undo the skb_reserve this proposed patch will make
sure the alignment skb_reserve is done once, upon allocating the skb and
not when taken out of the rx_recycle pool. Eliminating the need to undo
anything before queue skb back to the pool.
This patch will apply cleanly against the 2.6.32.15. Another patch will
be submitted separately for the current Linus tree.
-- Liberty
Signed-off-by: Eran Liberty <liberty@xxxxxxxxxxxx>
---
drivers/net/gianfar.c | 52 +++++++++++++++++++++++++-------------------------
1 file changed, 27 insertions(+), 25 deletions(-)
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -1568,6 +1568,15 @@
schedule_work(&priv->reset_task);
}
+static void gfar_align_skb(struct sk_buff *skb)
+{
+ /* We need the data buffer to be aligned properly. We will reserve
+ * as many bytes as needed to align the data properly
+ */
+ skb_reserve(skb, RXBUF_ALIGNMENT -
+ (((unsigned long) skb->data) & (RXBUF_ALIGNMENT - 1)));
+}
+
/* Interrupt Handler for Transmit complete */
static int gfar_clean_tx_ring(struct net_device *dev)
{
@@ -1620,9 +1629,10 @@
*/
if (skb_queue_len(&priv->rx_recycle) < priv->rx_ring_size &&
skb_recycle_check(skb, priv->rx_buffer_size +
- RXBUF_ALIGNMENT))
+ RXBUF_ALIGNMENT)) {
+ gfar_align_skb(skb);
__skb_queue_head(&priv->rx_recycle, skb);
- else
+ } else
dev_kfree_skb_any(skb);
priv->tx_skbuff[skb_dirtytx] = NULL;
@@ -1696,28 +1706,29 @@
bdp->lstatus = lstatus;
}
+static struct sk_buff * gfar_alloc_skb(struct net_device *dev)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ struct sk_buff *skb = NULL;
+
+ skb = netdev_alloc_skb(dev, priv->rx_buffer_size + RXBUF_ALIGNMENT);
+ if (!skb)
+ return NULL;
+
+ gfar_align_skb(skb);
+
+ return skb;
+}
struct sk_buff * gfar_new_skb(struct net_device *dev)
{
- unsigned int alignamount;
struct gfar_private *priv = netdev_priv(dev);
struct sk_buff *skb = NULL;
skb = __skb_dequeue(&priv->rx_recycle);
- if (!skb)
- skb = netdev_alloc_skb(dev,
- priv->rx_buffer_size + RXBUF_ALIGNMENT);
if (!skb)
- return NULL;
-
- alignamount = RXBUF_ALIGNMENT -
- (((unsigned long) skb->data) & (RXBUF_ALIGNMENT - 1));
-
- /* We need the data buffer to be aligned properly. We will reserve
- * as many bytes as needed to align the data properly
- */
- skb_reserve(skb, alignamount);
+ skb = gfar_alloc_skb(dev);
return skb;
}
@@ -1853,17 +1864,8 @@
if (unlikely(!newskb))
newskb = skb;
- else if (skb) {
- /*
- * We need to reset ->data to what it
- * was before gfar_new_skb() re-aligned
- * it to an RXBUF_ALIGNMENT boundary
- * before we put the skb back on the
- * recycle list.
- */
- skb->data = skb->head + NET_SKB_PAD;
+ else if (skb)
__skb_queue_head(&priv->rx_recycle, skb);
- }
} else {
/* Increment the number of packets */
dev->stats.rx_packets++;