From: Eric Dumazet on
Le mardi 30 mars 2010 à 11:34 +0200, Michal Simek a écrit :

> 2233 packets pruned from receive queue because of socket buffer overrun
> TCPRcvCollapsed: 207654

Thats a problem. A big one :(

If I remember, you use LL_TEMAC driver.

This drivers allocates big skbs for RX ring (more than 9000 bytes each
skb). Given your 32 Mbytes kernel size, this seems plain wrong.

You might try to copybreak them before giving skb to network stack,
consuming the minimum space.

This would also help this driver to survive in low memory conditions,
avoiding death if high order pages are not available.

I cannot even compile this driver on my x86 platform, but here is a
preliminar patch to give you the idea :


[PATCH] ll_temac: Fix some memory allocation problems

Driver use high order allocations that might fail after a while.
When receiving a buffer from card, try to copy it to keep a pool of
pre-allocated high order buffers.

Signed-off-by: Eric Dumazet <eric.dumazet(a)gmail.com>
---
Please please please note I did not test this patch.

drivers/net/ll_temac_main.c | 48 +++++++++++++++-------------------
1 file changed, 22 insertions(+), 26 deletions(-)
diff --git a/drivers/net/ll_temac_main.c b/drivers/net/ll_temac_main.c
index a18e348..412b72e 100644
--- a/drivers/net/ll_temac_main.c
+++ b/drivers/net/ll_temac_main.c
@@ -155,8 +155,8 @@ static int temac_dma_bd_init(struct net_device *ndev)
lp->rx_bd_v[i].next = lp->rx_bd_p +
sizeof(*lp->rx_bd_v) * ((i + 1) % RX_BD_NUM);

- skb = alloc_skb(XTE_MAX_JUMBO_FRAME_SIZE
- + XTE_ALIGN, GFP_ATOMIC);
+ skb = alloc_skb(XTE_MAX_JUMBO_FRAME_SIZE + XTE_ALIGN,
+ GFP_KERNEL);
if (skb == 0) {
dev_err(&ndev->dev, "alloc_skb error %d\n", i);
return -1;
@@ -625,34 +625,30 @@ static void ll_temac_recv(struct net_device *ndev)
skb = lp->rx_skb[lp->rx_bd_ci];
length = cur_p->app4 & 0x3FFF;

- skb_vaddr = virt_to_bus(skb->data);
+ new_skb = netdev_alloc_skb_ip_align(length);
+ if (new_skb) {
+ skb_copy_to_linear_data(new_skb, skb->data, length);
+ skb_put(new_skb, length);
+ skb_vaddr = virt_to_bus(skb->data);
+ dma_sync_single_for_device(ndev->dev.parent,
+ skb_vaddr,
+ XTE_MAX_JUMBO_FRAME_SIZE,
+ PCI_DMA_FROMDEVICE);
+ new_skb->dev = ndev;
+ new_skb->protocol = eth_type_trans(new_skb, ndev);
+ new_skb->ip_summed = CHECKSUM_NONE;
+
+ netif_rx(new_skb);
+
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += length;
+ } else
+ ndev->stats.rx_dropped++;
+
dma_unmap_single(ndev->dev.parent, skb_vaddr, length,
DMA_FROM_DEVICE);

- skb_put(skb, length);
- skb->dev = ndev;
- skb->protocol = eth_type_trans(skb, ndev);
- skb->ip_summed = CHECKSUM_NONE;
-
- netif_rx(skb);
-
- ndev->stats.rx_packets++;
- ndev->stats.rx_bytes += length;
-
- new_skb = alloc_skb(XTE_MAX_JUMBO_FRAME_SIZE + XTE_ALIGN,
- GFP_ATOMIC);
- if (new_skb == 0) {
- dev_err(&ndev->dev, "no memory for new sk_buff\n");
- spin_unlock_irqrestore(&lp->rx_lock, flags);
- return;
- }
-
- skb_reserve(new_skb, BUFFER_ALIGN(new_skb->data));
-
cur_p->app0 = STS_CTRL_APP0_IRQONEND;
- cur_p->phys = dma_map_single(ndev->dev.parent, new_skb->data,
- XTE_MAX_JUMBO_FRAME_SIZE,
- DMA_FROM_DEVICE);
cur_p->len = XTE_MAX_JUMBO_FRAME_SIZE;
lp->rx_skb[lp->rx_bd_ci] = new_skb;



--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/