diff -Naur linux-2.4.20-5.2.52/drivers/net/e1000/e1000_main.c linux-2.4.20-pb/drivers/net/e1000/e1000_main.c --- linux-2.4.20-5.2.52/drivers/net/e1000/e1000_main.c 2004-05-17 22:59:53.000000000 +0000 +++ linux-2.4.20-pb/drivers/net/e1000/e1000_main.c 2004-12-07 10:16:16.000000000 +0000 @@ -2319,9 +2323,9 @@ E1000_DBG("%s: Receive packet consumed multiple buffers\n", netdev->name); - dev_kfree_skb_irq(skb); + //dev_kfree_skb_irq(skb); //PB rx_desc->status = 0; - buffer_info->skb = NULL; + //buffer_info->skb = NULL; //PB if(++i == rx_ring->count) i = 0; @@ -2347,9 +2351,9 @@ length--; } else { - dev_kfree_skb_irq(skb); + //dev_kfree_skb_irq(skb); //PB rx_desc->status = 0; - buffer_info->skb = NULL; + //buffer_info->skb = NULL; //PB if(++i == rx_ring->count) i = 0; @@ -2393,7 +2398,7 @@ netdev->last_rx = jiffies; rx_desc->status = 0; - buffer_info->skb = NULL; + //buffer_info->skb = NULL; //PB: E1000 doesn't reallocate packets if(++i == rx_ring->count) i = 0; @@ -2421,20 +2426,37 @@ struct e1000_rx_desc *rx_desc; struct e1000_buffer *buffer_info; struct sk_buff *skb; - int reserve_len = 2; + int reserve_len = 18; unsigned int i; i = rx_ring->next_to_use; - buffer_info = &rx_ring->buffer_info[i]; - while(!buffer_info->skb) { + while(1) { + buffer_info = &rx_ring->buffer_info[i]; + if (!buffer_info->skb) { + ; /* try to allocate new buf */ + } else { + if (!skb_shared(buffer_info->skb)) { + ; /* try to reallocate unused buf */ + } else { + break; /* Better luck next round */ + } + } + rx_desc = E1000_RX_DESC(*rx_ring, i); - skb = dev_alloc_skb(adapter->rx_buffer_len + reserve_len); + if (!buffer_info->skb) { + /* TODO: optimize this alloc size */ + skb = alloc_skb(adapter->rx_buffer_len + reserve_len, GFP_ATOMIC); + } else { + skb = realloc_skb(buffer_info->skb, adapter->rx_buffer_len + reserve_len, GFP_ATOMIC); + } if(!skb) { - /* Better luck next round */ - break; + break; /* Better luck next round */ + } else { + skb->dev = netdev; + skb_get(skb); /* It's ours. Don't let others deallocate */ } /* Make buffer alignment 2 beyond a 16 byte boundary @@ -2443,8 +2465,6 @@ */ skb_reserve(skb, reserve_len); - skb->dev = netdev; - buffer_info->skb = skb; buffer_info->length = adapter->rx_buffer_len; buffer_info->dma = @@ -2466,7 +2486,6 @@ } if(++i == rx_ring->count) i = 0; - buffer_info = &rx_ring->buffer_info[i]; } rx_ring->next_to_use = i; diff -Naur linux-2.4.20-5.2.52/include/linux/skbuff.h linux-2.4.20-pb/include/linux/skbuff.h --- linux-2.4.20-5.2.52/include/linux/skbuff.h 2002-08-03 00:39:46.000000000 +0000 +++ linux-2.4.20-pb/include/linux/skbuff.h 2004-12-07 10:16:16.000000000 +0000 @@ -230,6 +230,7 @@ extern void __kfree_skb(struct sk_buff *skb); extern struct sk_buff * alloc_skb(unsigned int size, int priority); +extern struct sk_buff * realloc_skb(struct sk_buff *skb, unsigned int size, int priority); extern void kfree_skbmem(struct sk_buff *skb); extern struct sk_buff * skb_clone(struct sk_buff *skb, int priority); extern struct sk_buff * skb_copy(const struct sk_buff *skb, int priority); @@ -240,6 +241,7 @@ int newheadroom, int newtailroom, int priority); +extern struct sk_buff * skb_pad(struct sk_buff *skb, int pad); #define dev_kfree_skb(a) kfree_skb(a) extern void skb_over_panic(struct sk_buff *skb, int len, void *here); extern void skb_under_panic(struct sk_buff *skb, int len, void *here); @@ -1082,6 +1084,26 @@ } /** + * skb_padto - pad an skbuff up to a minimal size + * @skb: buffer to pad + * @len: minimal length + * + * Pads up a buffer to ensure the trailing bytes exist and are + * blanked. If the buffer already contains sufficient data it + * is untouched. Returns the buffer, which may be a replacement + * for the original, or NULL for out of memory - in which case + * the original buffer is still freed. + */ + +static inline struct sk_buff *skb_padto(struct sk_buff *skb, unsigned int len) +{ + unsigned int size = skb->len; + if(likely(size >= len)) + return skb; + return skb_pad(skb, len-size); +} + +/** * skb_linearize - convert paged skb to linear one * @skb: buffer to linarize * @gfp: allocation mode diff -Naur linux-2.4.20-5.2.52/net/core/skbuff.c linux-2.4.20-pb/net/core/skbuff.c --- linux-2.4.20-5.2.52/net/core/skbuff.c 2002-08-03 00:39:46.000000000 +0000 +++ linux-2.4.20-pb/net/core/skbuff.c 2004-12-07 10:16:16.000000000 +0000 @@ -216,7 +216,6 @@ return NULL; } - /* * Slab constructor for a skb head. */ @@ -251,6 +250,59 @@ #endif } +/** + * realloc_skb - reset skb for new packet. + * @size: size to allocate + * @gfp_mask: allocation mask + * + * Allocate a new &sk_buff. The returned buffer has no headroom and a + * tail room of size bytes. The object has a reference count of one. + * The return is the buffer. On a failure the return is %NULL. + * + * Buffers may only be allocated from interrupts using a @gfp_mask of + * %GFP_ATOMIC. + */ + +struct sk_buff *realloc_skb(struct sk_buff* skb, unsigned int size, int gfp_mask) +{ + int truesize=skb->truesize; + u8 *data=skb->head; + + skb_headerinit(skb, (kmem_cache_t *)NULL, 0); + + /* Get the DATA. Size must match skb_add_mtu(). */ + size = SKB_DATA_ALIGN(size); + if ((size+sizeof(struct sk_buff)) > truesize) { + skb_release_data(skb); + data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask); + if (data == NULL) + goto nodata; + } + + /* XXX: does not include slab overhead */ + skb->truesize = size + sizeof(struct sk_buff); + + /* Load the data pointers. */ + skb->head = data; + skb->data = data; + skb->tail = data; + skb->end = data + size; + + /* Set up other state */ + skb->len = 0; + skb->cloned = 0; + skb->data_len = 0; + + atomic_set(&skb->users, 1); + atomic_set(&(skb_shinfo(skb)->dataref), 1); + skb_shinfo(skb)->nr_frags = 0; + skb_shinfo(skb)->frag_list = NULL; + return skb; + +nodata: + return NULL; +} + static void skb_drop_fraglist(struct sk_buff *skb) { struct sk_buff *list = skb_shinfo(skb)->frag_list; @@ -731,6 +783,36 @@ return n; } +/** + * skb_pad - zero pad the tail of an skb + * @skb: buffer to pad + * @pad: space to pad + * + * Ensure that a buffer is followed by a padding area that is zero + * filled. Used by network drivers which may DMA or transfer data + * beyond the buffer end onto the wire. + * + * May return NULL in out of memory cases. + */ + +struct sk_buff *skb_pad(struct sk_buff *skb, int pad) +{ + struct sk_buff *nskb; + + /* If the skbuff is non linear tailroom is always zero.. */ + if(skb_tailroom(skb) >= pad) + { + memset(skb->data+skb->len, 0, pad); + return skb; + } + + nskb = skb_copy_expand(skb, skb_headroom(skb), skb_tailroom(skb) + pad, GFP_ATOMIC); + kfree_skb(skb); + if(nskb) + memset(nskb->data+nskb->len, 0, pad); + return nskb; +} + /* Trims skb to length len. It can change skb pointers, if "realloc" is 1. * If realloc==0 and trimming is impossible without change of data, * it is BUG().