dma_unmap_single(&bp->pdev->dev, dma_unmap_addr(rx_buf, mapping),
                         fp->rx_buf_size, DMA_FROM_DEVICE);
        if (likely(new_data))
-               skb = build_skb(data);
+               skb = build_skb(data, 0);
 
        if (likely(skb)) {
 #ifdef BNX2X_STOP_ON_ERROR
                                                 dma_unmap_addr(rx_buf, mapping),
                                                 fp->rx_buf_size,
                                                 DMA_FROM_DEVICE);
-                               skb = build_skb(data);
+                               skb = build_skb(data, 0);
                                if (unlikely(!skb)) {
                                        kfree(data);
                                        fp->eth_q_stats.rx_skb_alloc_failed++;
 
        __u8                    wifi_acked_valid:1;
        __u8                    wifi_acked:1;
        __u8                    no_fcs:1;
-       /* 9/11 bit hole (depending on ndisc_nodetype presence) */
+       __u8                    head_frag:1;
+       /* 8/10 bit hole (depending on ndisc_nodetype presence) */
        kmemcheck_bitfield_end(flags2);
 
 #ifdef CONFIG_NET_DMA
 extern void           __kfree_skb(struct sk_buff *skb);
 extern struct sk_buff *__alloc_skb(unsigned int size,
                                   gfp_t priority, int fclone, int node);
-extern struct sk_buff *build_skb(void *data);
+extern struct sk_buff *build_skb(void *data, unsigned int frag_size);
 static inline struct sk_buff *alloc_skb(unsigned int size,
                                        gfp_t priority)
 {
 
 /**
  * build_skb - build a network buffer
  * @data: data buffer provided by caller
+ * @frag_size: size of fragment, or 0 if head was kmalloced
  *
  * Allocate a new &sk_buff. Caller provides space holding head and
  * skb_shared_info. @data must have been allocated by kmalloc()
  *  before giving packet to stack.
  *  RX rings only contains data buffers, not full skbs.
  */
-struct sk_buff *build_skb(void *data)
+struct sk_buff *build_skb(void *data, unsigned int frag_size)
 {
        struct skb_shared_info *shinfo;
        struct sk_buff *skb;
-       unsigned int size;
+       unsigned int size = frag_size ? : ksize(data);
 
        skb = kmem_cache_alloc(skbuff_head_cache, GFP_ATOMIC);
        if (!skb)
                return NULL;
 
-       size = ksize(data) - SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+       size -= SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
 
        memset(skb, 0, offsetof(struct sk_buff, tail));
        skb->truesize = SKB_TRUESIZE(size);
+       skb->head_frag = frag_size != 0;
        atomic_set(&skb->users, 1);
        skb->head = data;
        skb->data = data;
                skb_get(list);
 }
 
+static void skb_free_head(struct sk_buff *skb)
+{
+       if (skb->head_frag)
+               put_page(virt_to_head_page(skb->head));
+       else
+               kfree(skb->head);
+}
+
 static void skb_release_data(struct sk_buff *skb)
 {
        if (!skb->cloned ||
                if (skb_has_frag_list(skb))
                        skb_drop_fraglist(skb);
 
-               kfree(skb->head);
+               skb_free_head(skb);
        }
 }
 
        C(tail);
        C(end);
        C(head);
+       C(head_frag);
        C(data);
        C(truesize);
        atomic_set(&n->users, 1);
                fastpath = atomic_read(&skb_shinfo(skb)->dataref) == delta;
        }
 
-       if (fastpath &&
+       if (fastpath && !skb->head_frag &&
            size + sizeof(struct skb_shared_info) <= ksize(skb->head)) {
                memmove(skb->head + size, skb_shinfo(skb),
                        offsetof(struct skb_shared_info,
               offsetof(struct skb_shared_info, frags[skb_shinfo(skb)->nr_frags]));
 
        if (fastpath) {
-               kfree(skb->head);
+               skb_free_head(skb);
        } else {
                /* copy this zero copy skb frags */
                if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) {
        off = (data + nhead) - skb->head;
 
        skb->head     = data;
+       skb->head_frag = 0;
 adjust_others:
        skb->data    += off;
 #ifdef NET_SKBUFF_DATA_USES_OFFSET