[NET] CORE: Introducing new memory accounting interface.
[safe/jmp/linux-2.6] / net / core / skbuff.c
index a48b086..98420f9 100644 (file)
 #endif
 #include <linux/string.h>
 #include <linux/skbuff.h>
+#include <linux/splice.h>
 #include <linux/cache.h>
 #include <linux/rtnetlink.h>
 #include <linux/init.h>
+#include <linux/scatterlist.h>
 
 #include <net/protocol.h>
 #include <net/dst.h>
 static struct kmem_cache *skbuff_head_cache __read_mostly;
 static struct kmem_cache *skbuff_fclone_cache __read_mostly;
 
+static void sock_pipe_buf_release(struct pipe_inode_info *pipe,
+                                 struct pipe_buffer *buf)
+{
+       struct sk_buff *skb = (struct sk_buff *) buf->private;
+
+       kfree_skb(skb);
+}
+
+static void sock_pipe_buf_get(struct pipe_inode_info *pipe,
+                               struct pipe_buffer *buf)
+{
+       struct sk_buff *skb = (struct sk_buff *) buf->private;
+
+       skb_get(skb);
+}
+
+static int sock_pipe_buf_steal(struct pipe_inode_info *pipe,
+                              struct pipe_buffer *buf)
+{
+       return 1;
+}
+
+
+/* Pipe buffer operations for a socket. */
+static struct pipe_buf_operations sock_pipe_buf_ops = {
+       .can_merge = 0,
+       .map = generic_pipe_buf_map,
+       .unmap = generic_pipe_buf_unmap,
+       .confirm = generic_pipe_buf_confirm,
+       .release = sock_pipe_buf_release,
+       .steal = sock_pipe_buf_steal,
+       .get = sock_pipe_buf_get,
+};
+
 /*
  *     Keep out-of-line to prevent kernel bloat.
  *     __builtin_return_address is not used because it is not always
@@ -87,8 +123,9 @@ static struct kmem_cache *skbuff_fclone_cache __read_mostly;
 void skb_over_panic(struct sk_buff *skb, int sz, void *here)
 {
        printk(KERN_EMERG "skb_over_panic: text:%p len:%d put:%d head:%p "
-                         "data:%p tail:%p end:%p dev:%s\n",
-              here, skb->len, sz, skb->head, skb->data, skb->tail, skb->end,
+                         "data:%p tail:%#lx end:%#lx dev:%s\n",
+              here, skb->len, sz, skb->head, skb->data,
+              (unsigned long)skb->tail, (unsigned long)skb->end,
               skb->dev ? skb->dev->name : "<NULL>");
        BUG();
 }
@@ -105,8 +142,9 @@ void skb_over_panic(struct sk_buff *skb, int sz, void *here)
 void skb_under_panic(struct sk_buff *skb, int sz, void *here)
 {
        printk(KERN_EMERG "skb_under_panic: text:%p len:%d put:%d head:%p "
-                         "data:%p tail:%p end:%p dev:%s\n",
-              here, skb->len, sz, skb->head, skb->data, skb->tail, skb->end,
+                         "data:%p tail:%#lx end:%#lx dev:%s\n",
+              here, skb->len, sz, skb->head, skb->data,
+              (unsigned long)skb->tail, (unsigned long)skb->end,
               skb->dev ? skb->dev->name : "<NULL>");
        BUG();
 }
@@ -155,20 +193,22 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
        if (!skb)
                goto out;
 
-       /* Get the DATA. Size must match skb_add_mtu(). */
        size = SKB_DATA_ALIGN(size);
        data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info),
                        gfp_mask, node);
        if (!data)
                goto nodata;
 
-       memset(skb, 0, offsetof(struct sk_buff, truesize));
+       /*
+        * See comment in sk_buff definition, just before the 'tail' member
+        */
+       memset(skb, 0, offsetof(struct sk_buff, tail));
        skb->truesize = size + sizeof(struct sk_buff);
        atomic_set(&skb->users, 1);
        skb->head = data;
        skb->data = data;
-       skb->tail = data;
-       skb->end  = data + size;
+       skb_reset_tail_pointer(skb);
+       skb->end = skb->tail + size;
        /* make sure we initialize shinfo sequentially */
        shinfo = skb_shinfo(skb);
        atomic_set(&shinfo->dataref, 1);
@@ -270,12 +310,11 @@ static void skb_release_data(struct sk_buff *skb)
 /*
  *     Free an skbuff by memory without cleaning the state.
  */
-void kfree_skbmem(struct sk_buff *skb)
+static void kfree_skbmem(struct sk_buff *skb)
 {
        struct sk_buff *other;
        atomic_t *fclone_ref;
 
-       skb_release_data(skb);
        switch (skb->fclone) {
        case SKB_FCLONE_UNAVAILABLE:
                kmem_cache_free(skbuff_head_cache, skb);
@@ -299,19 +338,11 @@ void kfree_skbmem(struct sk_buff *skb)
                if (atomic_dec_and_test(fclone_ref))
                        kmem_cache_free(skbuff_fclone_cache, other);
                break;
-       };
+       }
 }
 
-/**
- *     __kfree_skb - private function
- *     @skb: buffer
- *
- *     Free an sk_buff. Release anything attached to the buffer.
- *     Clean the state. This is an internal helper function. Users should
- *     always call kfree_skb
- */
-
-void __kfree_skb(struct sk_buff *skb)
+/* Free everything but the sk_buff shell. */
+static void skb_release_all(struct sk_buff *skb)
 {
        dst_release(skb->dst);
 #ifdef CONFIG_XFRM
@@ -321,15 +352,13 @@ void __kfree_skb(struct sk_buff *skb)
                WARN_ON(in_irq());
                skb->destructor(skb);
        }
-#ifdef CONFIG_NETFILTER
-       nf_conntrack_put(skb->nfct);
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+       nf_conntrack_put(skb->nfct);
        nf_conntrack_put_reasm(skb->nfct_reasm);
 #endif
 #ifdef CONFIG_BRIDGE_NETFILTER
        nf_bridge_put(skb->nf_bridge);
 #endif
-#endif
 /* XXX: IS this still necessary? - JHS */
 #ifdef CONFIG_NET_SCHED
        skb->tc_index = 0;
@@ -337,7 +366,21 @@ void __kfree_skb(struct sk_buff *skb)
        skb->tc_verd = 0;
 #endif
 #endif
+       skb_release_data(skb);
+}
+
+/**
+ *     __kfree_skb - private function
+ *     @skb: buffer
+ *
+ *     Free an sk_buff. Release anything attached to the buffer.
+ *     Clean the state. This is an internal helper function. Users should
+ *     always call kfree_skb
+ */
 
+void __kfree_skb(struct sk_buff *skb)
+{
+       skb_release_all(skb);
        kfree_skbmem(skb);
 }
 
@@ -359,6 +402,91 @@ void kfree_skb(struct sk_buff *skb)
        __kfree_skb(skb);
 }
 
+static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
+{
+       new->tstamp             = old->tstamp;
+       new->dev                = old->dev;
+       new->transport_header   = old->transport_header;
+       new->network_header     = old->network_header;
+       new->mac_header         = old->mac_header;
+       new->dst                = dst_clone(old->dst);
+#ifdef CONFIG_INET
+       new->sp                 = secpath_get(old->sp);
+#endif
+       memcpy(new->cb, old->cb, sizeof(old->cb));
+       new->csum_start         = old->csum_start;
+       new->csum_offset        = old->csum_offset;
+       new->local_df           = old->local_df;
+       new->pkt_type           = old->pkt_type;
+       new->ip_summed          = old->ip_summed;
+       skb_copy_queue_mapping(new, old);
+       new->priority           = old->priority;
+#if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
+       new->ipvs_property      = old->ipvs_property;
+#endif
+       new->protocol           = old->protocol;
+       new->mark               = old->mark;
+       __nf_copy(new, old);
+#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
+    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
+       new->nf_trace           = old->nf_trace;
+#endif
+#ifdef CONFIG_NET_SCHED
+       new->tc_index           = old->tc_index;
+#ifdef CONFIG_NET_CLS_ACT
+       new->tc_verd            = old->tc_verd;
+#endif
+#endif
+       skb_copy_secmark(new, old);
+}
+
+static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb)
+{
+#define C(x) n->x = skb->x
+
+       n->next = n->prev = NULL;
+       n->sk = NULL;
+       __copy_skb_header(n, skb);
+
+       C(len);
+       C(data_len);
+       C(mac_len);
+       n->hdr_len = skb->nohdr ? skb_headroom(skb) : skb->hdr_len;
+       n->cloned = 1;
+       n->nohdr = 0;
+       n->destructor = NULL;
+       C(iif);
+       C(tail);
+       C(end);
+       C(head);
+       C(data);
+       C(truesize);
+       atomic_set(&n->users, 1);
+
+       atomic_inc(&(skb_shinfo(skb)->dataref));
+       skb->cloned = 1;
+
+       return n;
+#undef C
+}
+
+/**
+ *     skb_morph       -       morph one skb into another
+ *     @dst: the skb to receive the contents
+ *     @src: the skb to supply the contents
+ *
+ *     This is identical to skb_clone except that the target skb is
+ *     supplied by the user.
+ *
+ *     The target skb is returned upon exit.
+ */
+struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src)
+{
+       skb_release_all(dst);
+       return __skb_clone(dst, src);
+}
+EXPORT_SYMBOL_GPL(skb_morph);
+
 /**
  *     skb_clone       -       duplicate an sk_buff
  *     @skb: buffer to clone
@@ -390,60 +518,7 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
                n->fclone = SKB_FCLONE_UNAVAILABLE;
        }
 
-#define C(x) n->x = skb->x
-
-       n->next = n->prev = NULL;
-       n->sk = NULL;
-       C(tstamp);
-       C(dev);
-       C(transport_header);
-       C(network_header);
-       C(mac_header);
-       C(dst);
-       dst_clone(skb->dst);
-       C(sp);
-#ifdef CONFIG_INET
-       secpath_get(skb->sp);
-#endif
-       memcpy(n->cb, skb->cb, sizeof(skb->cb));
-       C(len);
-       C(data_len);
-       C(mac_len);
-       C(csum);
-       C(local_df);
-       n->cloned = 1;
-       n->nohdr = 0;
-       C(pkt_type);
-       C(ip_summed);
-       C(priority);
-#if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
-       C(ipvs_property);
-#endif
-       C(protocol);
-       n->destructor = NULL;
-       C(mark);
-       __nf_copy(n, skb);
-#ifdef CONFIG_NET_SCHED
-       C(tc_index);
-#ifdef CONFIG_NET_CLS_ACT
-       n->tc_verd = SET_TC_VERD(skb->tc_verd,0);
-       n->tc_verd = CLR_TC_OK2MUNGE(n->tc_verd);
-       n->tc_verd = CLR_TC_MUNGED(n->tc_verd);
-       C(iif);
-#endif
-       skb_copy_secmark(n, skb);
-#endif
-       C(truesize);
-       atomic_set(&n->users, 1);
-       C(head);
-       C(data);
-       C(tail);
-       C(end);
-
-       atomic_inc(&(skb_shinfo(skb)->dataref));
-       skb->cloned = 1;
-
-       return n;
+       return __skb_clone(n, skb);
 }
 
 static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
@@ -454,42 +529,15 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
         */
        unsigned long offset = new->data - old->data;
 #endif
-       new->sk         = NULL;
-       new->dev        = old->dev;
-       new->priority   = old->priority;
-       new->protocol   = old->protocol;
-       new->dst        = dst_clone(old->dst);
-#ifdef CONFIG_INET
-       new->sp         = secpath_get(old->sp);
-#endif
-       new->transport_header = old->transport_header;
-       new->network_header   = old->network_header;
-       new->mac_header       = old->mac_header;
+
+       __copy_skb_header(new, old);
+
 #ifndef NET_SKBUFF_DATA_USES_OFFSET
        /* {transport,network,mac}_header are relative to skb->head */
        new->transport_header += offset;
        new->network_header   += offset;
        new->mac_header       += offset;
 #endif
-       memcpy(new->cb, old->cb, sizeof(old->cb));
-       new->local_df   = old->local_df;
-       new->fclone     = SKB_FCLONE_UNAVAILABLE;
-       new->pkt_type   = old->pkt_type;
-       new->tstamp     = old->tstamp;
-       new->destructor = NULL;
-       new->mark       = old->mark;
-       __nf_copy(new, old);
-#if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
-       new->ipvs_property = old->ipvs_property;
-#endif
-#ifdef CONFIG_NET_SCHED
-#ifdef CONFIG_NET_CLS_ACT
-       new->tc_verd = old->tc_verd;
-#endif
-       new->tc_index   = old->tc_index;
-#endif
-       skb_copy_secmark(new, old);
-       atomic_set(&new->users, 1);
        skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size;
        skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs;
        skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type;
@@ -518,8 +566,12 @@ struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask)
        /*
         *      Allocate the copy buffer
         */
-       struct sk_buff *n = alloc_skb(skb->end - skb->head + skb->data_len,
-                                     gfp_mask);
+       struct sk_buff *n;
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+       n = alloc_skb(skb->end + skb->data_len, gfp_mask);
+#else
+       n = alloc_skb(skb->end - skb->head + skb->data_len, gfp_mask);
+#endif
        if (!n)
                return NULL;
 
@@ -527,8 +579,6 @@ struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask)
        skb_reserve(n, headerlen);
        /* Set the tail pointer and length */
        skb_put(n, skb->len);
-       n->csum      = skb->csum;
-       n->ip_summed = skb->ip_summed;
 
        if (skb_copy_bits(skb, -headerlen, n->head, headerlen + skb->len))
                BUG();
@@ -556,8 +606,12 @@ struct sk_buff *pskb_copy(struct sk_buff *skb, gfp_t gfp_mask)
        /*
         *      Allocate the copy buffer
         */
-       struct sk_buff *n = alloc_skb(skb->end - skb->head, gfp_mask);
-
+       struct sk_buff *n;
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+       n = alloc_skb(skb->end, gfp_mask);
+#else
+       n = alloc_skb(skb->end - skb->head, gfp_mask);
+#endif
        if (!n)
                goto out;
 
@@ -566,9 +620,7 @@ struct sk_buff *pskb_copy(struct sk_buff *skb, gfp_t gfp_mask)
        /* Set the tail pointer and length */
        skb_put(n, skb_headlen(skb));
        /* Copy the bytes */
-       memcpy(n->data, skb->data, n->len);
-       n->csum      = skb->csum;
-       n->ip_summed = skb->ip_summed;
+       skb_copy_from_linear_data(skb, n->data, n->len);
 
        n->truesize += skb->data_len;
        n->data_len  = skb->data_len;
@@ -615,7 +667,11 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
 {
        int i;
        u8 *data;
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+       int size = nhead + skb->end + ntail;
+#else
        int size = nhead + (skb->end - skb->head) + ntail;
+#endif
        long off;
 
        if (skb_shared(skb))
@@ -629,8 +685,13 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
 
        /* Copy only real data... and, alas, header. This should be
         * optimized for the cases when header is void. */
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+       memcpy(data + nhead, skb->head, skb->tail);
+#else
        memcpy(data + nhead, skb->head, skb->tail - skb->head);
-       memcpy(data + size, skb->end, sizeof(struct skb_shared_info));
+#endif
+       memcpy(data + size, skb_end_pointer(skb),
+              sizeof(struct skb_shared_info));
 
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
                get_page(skb_shinfo(skb)->frags[i].page);
@@ -643,16 +704,21 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
        off = (data + nhead) - skb->head;
 
        skb->head     = data;
-       skb->end      = data + size;
        skb->data    += off;
-       skb->tail    += off;
-#ifndef NET_SKBUFF_DATA_USES_OFFSET
-       /* {transport,network,mac}_header are relative to skb->head */
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+       skb->end      = size;
+       off           = nhead;
+#else
+       skb->end      = skb->head + size;
+#endif
+       /* {transport,network,mac}_header and tail are relative to skb->head */
+       skb->tail             += off;
        skb->transport_header += off;
        skb->network_header   += off;
        skb->mac_header       += off;
-#endif
+       skb->csum_start       += nhead;
        skb->cloned   = 0;
+       skb->hdr_len  = 0;
        skb->nohdr    = 0;
        atomic_set(&skb_shinfo(skb)->dataref, 1);
        return 0;
@@ -699,9 +765,6 @@ struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom)
  *
  *     You must pass %GFP_ATOMIC as the allocation priority if this function
  *     is called from an interrupt.
- *
- *     BUG ALERT: ip_summed is not copied. Why does this work? Is it used
- *     only by netfilter in the cases when checksum is recalculated? --ANK
  */
 struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
                                int newheadroom, int newtailroom,
@@ -712,7 +775,9 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
         */
        struct sk_buff *n = alloc_skb(newheadroom + skb->len + newtailroom,
                                      gfp_mask);
+       int oldheadroom = skb_headroom(skb);
        int head_copy_len, head_copy_off;
+       int off;
 
        if (!n)
                return NULL;
@@ -722,7 +787,7 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
        /* Set the tail pointer and length */
        skb_put(n, skb->len);
 
-       head_copy_len = skb_headroom(skb);
+       head_copy_len = oldheadroom;
        head_copy_off = 0;
        if (newheadroom <= head_copy_len)
                head_copy_len = newheadroom;
@@ -736,6 +801,14 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
 
        copy_skb_header(n, skb);
 
+       off                  = newheadroom - oldheadroom;
+       n->csum_start       += off;
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+       n->transport_header += off;
+       n->network_header   += off;
+       n->mac_header       += off;
+#endif
+
        return n;
 }
 
@@ -863,7 +936,7 @@ done:
        } else {
                skb->len       = len;
                skb->data_len  = 0;
-               skb->tail      = skb->data + len;
+               skb_set_tail_pointer(skb, len);
        }
 
        return 0;
@@ -908,7 +981,7 @@ unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta)
                        return NULL;
        }
 
-       if (skb_copy_bits(skb, skb_headlen(skb), skb->tail, delta))
+       if (skb_copy_bits(skb, skb_headlen(skb), skb_tail_pointer(skb), delta))
                BUG();
 
        /* Optimization: no fragments, no reasons to preestimate
@@ -1004,7 +1077,7 @@ pull_pages:
        skb->tail     += delta;
        skb->data_len -= delta;
 
-       return skb->tail;
+       return skb_tail_pointer(skb);
 }
 
 /* Copy some data bits from skb to kernel buffer. */
@@ -1021,7 +1094,7 @@ int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
        if ((copy = start - offset) > 0) {
                if (copy > len)
                        copy = len;
-               memcpy(to, skb->data + offset, copy);
+               skb_copy_from_linear_data_offset(skb, offset, to, copy);
                if ((len -= copy) == 0)
                        return 0;
                offset += copy;
@@ -1084,6 +1157,217 @@ fault:
        return -EFAULT;
 }
 
+/*
+ * Callback from splice_to_pipe(), if we need to release some pages
+ * at the end of the spd in case we error'ed out in filling the pipe.
+ */
+static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i)
+{
+       struct sk_buff *skb = (struct sk_buff *) spd->partial[i].private;
+
+       kfree_skb(skb);
+}
+
+/*
+ * Fill page/offset/length into spd, if it can hold more pages.
+ */
+static inline int spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
+                               unsigned int len, unsigned int offset,
+                               struct sk_buff *skb)
+{
+       if (unlikely(spd->nr_pages == PIPE_BUFFERS))
+               return 1;
+
+       spd->pages[spd->nr_pages] = page;
+       spd->partial[spd->nr_pages].len = len;
+       spd->partial[spd->nr_pages].offset = offset;
+       spd->partial[spd->nr_pages].private = (unsigned long) skb_get(skb);
+       spd->nr_pages++;
+       return 0;
+}
+
+/*
+ * Map linear and fragment data from the skb to spd. Returns number of
+ * pages mapped.
+ */
+static int __skb_splice_bits(struct sk_buff *skb, unsigned int *offset,
+                            unsigned int *total_len,
+                            struct splice_pipe_desc *spd)
+{
+       unsigned int nr_pages = spd->nr_pages;
+       unsigned int poff, plen, len, toff, tlen;
+       int headlen, seg;
+
+       toff = *offset;
+       tlen = *total_len;
+       if (!tlen)
+               goto err;
+
+       /*
+        * if the offset is greater than the linear part, go directly to
+        * the fragments.
+        */
+       headlen = skb_headlen(skb);
+       if (toff >= headlen) {
+               toff -= headlen;
+               goto map_frag;
+       }
+
+       /*
+        * first map the linear region into the pages/partial map, skipping
+        * any potential initial offset.
+        */
+       len = 0;
+       while (len < headlen) {
+               void *p = skb->data + len;
+
+               poff = (unsigned long) p & (PAGE_SIZE - 1);
+               plen = min_t(unsigned int, headlen - len, PAGE_SIZE - poff);
+               len += plen;
+
+               if (toff) {
+                       if (plen <= toff) {
+                               toff -= plen;
+                               continue;
+                       }
+                       plen -= toff;
+                       poff += toff;
+                       toff = 0;
+               }
+
+               plen = min(plen, tlen);
+               if (!plen)
+                       break;
+
+               /*
+                * just jump directly to update and return, no point
+                * in going over fragments when the output is full.
+                */
+               if (spd_fill_page(spd, virt_to_page(p), plen, poff, skb))
+                       goto done;
+
+               tlen -= plen;
+       }
+
+       /*
+        * then map the fragments
+        */
+map_frag:
+       for (seg = 0; seg < skb_shinfo(skb)->nr_frags; seg++) {
+               const skb_frag_t *f = &skb_shinfo(skb)->frags[seg];
+
+               plen = f->size;
+               poff = f->page_offset;
+
+               if (toff) {
+                       if (plen <= toff) {
+                               toff -= plen;
+                               continue;
+                       }
+                       plen -= toff;
+                       poff += toff;
+                       toff = 0;
+               }
+
+               plen = min(plen, tlen);
+               if (!plen)
+                       break;
+
+               if (spd_fill_page(spd, f->page, plen, poff, skb))
+                       break;
+
+               tlen -= plen;
+       }
+
+done:
+       if (spd->nr_pages - nr_pages) {
+               *offset = 0;
+               *total_len = tlen;
+               return 0;
+       }
+err:
+       return 1;
+}
+
+/*
+ * Map data from the skb to a pipe. Should handle both the linear part,
+ * the fragments, and the frag list. It does NOT handle frag lists within
+ * the frag list, if such a thing exists. We'd probably need to recurse to
+ * handle that cleanly.
+ */
+int skb_splice_bits(struct sk_buff *__skb, unsigned int offset,
+                   struct pipe_inode_info *pipe, unsigned int tlen,
+                   unsigned int flags)
+{
+       struct partial_page partial[PIPE_BUFFERS];
+       struct page *pages[PIPE_BUFFERS];
+       struct splice_pipe_desc spd = {
+               .pages = pages,
+               .partial = partial,
+               .flags = flags,
+               .ops = &sock_pipe_buf_ops,
+               .spd_release = sock_spd_release,
+       };
+       struct sk_buff *skb;
+
+       /*
+        * I'd love to avoid the clone here, but tcp_read_sock()
+        * ignores reference counts and unconditonally kills the sk_buff
+        * on return from the actor.
+        */
+       skb = skb_clone(__skb, GFP_KERNEL);
+       if (unlikely(!skb))
+               return -ENOMEM;
+
+       /*
+        * __skb_splice_bits() only fails if the output has no room left,
+        * so no point in going over the frag_list for the error case.
+        */
+       if (__skb_splice_bits(skb, &offset, &tlen, &spd))
+               goto done;
+       else if (!tlen)
+               goto done;
+
+       /*
+        * now see if we have a frag_list to map
+        */
+       if (skb_shinfo(skb)->frag_list) {
+               struct sk_buff *list = skb_shinfo(skb)->frag_list;
+
+               for (; list && tlen; list = list->next) {
+                       if (__skb_splice_bits(list, &offset, &tlen, &spd))
+                               break;
+               }
+       }
+
+done:
+       /*
+        * drop our reference to the clone, the pipe consumption will
+        * drop the rest.
+        */
+       kfree_skb(skb);
+
+       if (spd.nr_pages) {
+               int ret;
+
+               /*
+                * Drop the socket lock, otherwise we have reverse
+                * locking dependencies between sk_lock and i_mutex
+                * here as compared to sendfile(). We enter here
+                * with the socket lock held, and splice_to_pipe() will
+                * grab the pipe inode lock. For sendfile() emulation,
+                * we call into ->sendpage() with the i_mutex lock held
+                * and networking will grab the socket lock.
+                */
+               release_sock(__skb->sk);
+               ret = splice_to_pipe(pipe, &spd);
+               lock_sock(__skb->sk);
+               return ret;
+       }
+
+       return 0;
+}
+
 /**
  *     skb_store_bits - store bits from kernel buffer to skb
  *     @skb: destination buffer
@@ -1096,7 +1380,7 @@ fault:
  *     traversing fragment lists and such.
  */
 
-int skb_store_bits(const struct sk_buff *skb, int offset, void *from, int len)
+int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len)
 {
        int i, copy;
        int start = skb_headlen(skb);
@@ -1107,7 +1391,7 @@ int skb_store_bits(const struct sk_buff *skb, int offset, void *from, int len)
        if ((copy = start - offset) > 0) {
                if (copy > len)
                        copy = len;
-               memcpy(skb->data + offset, from, copy);
+               skb_copy_to_linear_data_offset(skb, offset, from, copy);
                if ((len -= copy) == 0)
                        return 0;
                offset += copy;
@@ -1334,13 +1618,13 @@ void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to)
        long csstart;
 
        if (skb->ip_summed == CHECKSUM_PARTIAL)
-               csstart = skb_transport_offset(skb);
+               csstart = skb->csum_start - skb_headroom(skb);
        else
                csstart = skb_headlen(skb);
 
        BUG_ON(csstart > skb_headlen(skb));
 
-       memcpy(to, skb->data, csstart);
+       skb_copy_from_linear_data(skb, to, csstart);
 
        csum = 0;
        if (csstart != skb->len)
@@ -1508,27 +1792,14 @@ void skb_insert(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head
        spin_unlock_irqrestore(&list->lock, flags);
 }
 
-#if 0
-/*
- *     Tune the memory allocator for a new MTU size.
- */
-void skb_add_mtu(int mtu)
-{
-       /* Must match allocation in alloc_skb */
-       mtu = SKB_DATA_ALIGN(mtu) + sizeof(struct skb_shared_info);
-
-       kmem_add_cache_size(mtu);
-}
-#endif
-
 static inline void skb_split_inside_header(struct sk_buff *skb,
                                           struct sk_buff* skb1,
                                           const u32 len, const int pos)
 {
        int i;
 
-       memcpy(skb_put(skb1, pos - len), skb->data + len, pos - len);
-
+       skb_copy_from_linear_data_offset(skb, len, skb_put(skb1, pos - len),
+                                        pos - len);
        /* And move data appendix as is. */
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
                skb_shinfo(skb1)->frags[i] = skb_shinfo(skb)->frags[i];
@@ -1539,7 +1810,7 @@ static inline void skb_split_inside_header(struct sk_buff *skb,
        skb1->len                  += skb1->data_len;
        skb->data_len              = 0;
        skb->len                   = len;
-       skb->tail                  = skb->data + len;
+       skb_set_tail_pointer(skb, len);
 }
 
 static inline void skb_split_no_header(struct sk_buff *skb,
@@ -1687,6 +1958,11 @@ next_skb:
                st->stepped_offset += frag->size;
        }
 
+       if (st->frag_data) {
+               kunmap_skb_frag(st->frag_data);
+               st->frag_data = NULL;
+       }
+
        if (st->cur_skb->next) {
                st->cur_skb = st->cur_skb->next;
                st->frag_idx = 0;
@@ -1906,6 +2182,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
                tail = nskb;
 
                nskb->dev = skb->dev;
+               skb_copy_queue_mapping(nskb, skb);
                nskb->priority = skb->priority;
                nskb->protocol = skb->protocol;
                nskb->dst = dst_clone(skb->dst);
@@ -1918,8 +2195,8 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
                skb_set_network_header(nskb, skb->mac_len);
                nskb->transport_header = (nskb->network_header +
                                          skb_network_header_len(skb));
-               memcpy(skb_put(nskb, doffset), skb->data, doffset);
-
+               skb_copy_from_linear_data(skb, skb_put(nskb, doffset),
+                                         doffset);
                if (!sg) {
                        nskb->csum = skb_copy_and_csum_bits(skb, offset,
                                                            skb_put(nskb, len),
@@ -1932,7 +2209,8 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
 
                nskb->ip_summed = CHECKSUM_PARTIAL;
                nskb->csum = skb->csum;
-               memcpy(skb_put(nskb, hsize), skb->data + offset, hsize);
+               skb_copy_from_linear_data_offset(skb, offset,
+                                                skb_put(nskb, hsize), hsize);
 
                while (pos < offset + len) {
                        BUG_ON(i >= nfrags);
@@ -1983,13 +2261,204 @@ void __init skb_init(void)
                                              sizeof(struct sk_buff),
                                              0,
                                              SLAB_HWCACHE_ALIGN|SLAB_PANIC,
-                                             NULL, NULL);
+                                             NULL);
        skbuff_fclone_cache = kmem_cache_create("skbuff_fclone_cache",
                                                (2*sizeof(struct sk_buff)) +
                                                sizeof(atomic_t),
                                                0,
                                                SLAB_HWCACHE_ALIGN|SLAB_PANIC,
-                                               NULL, NULL);
+                                               NULL);
+}
+
+/**
+ *     skb_to_sgvec - Fill a scatter-gather list from a socket buffer
+ *     @skb: Socket buffer containing the buffers to be mapped
+ *     @sg: The scatter-gather list to map into
+ *     @offset: The offset into the buffer's contents to start mapping
+ *     @len: Length of buffer space to be mapped
+ *
+ *     Fill the specified scatter-gather list with mappings/pointers into a
+ *     region of the buffer space attached to a socket buffer.
+ */
+static int
+__skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
+{
+       int start = skb_headlen(skb);
+       int i, copy = start - offset;
+       int elt = 0;
+
+       if (copy > 0) {
+               if (copy > len)
+                       copy = len;
+               sg_set_buf(sg, skb->data + offset, copy);
+               elt++;
+               if ((len -= copy) == 0)
+                       return elt;
+               offset += copy;
+       }
+
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               int end;
+
+               BUG_TRAP(start <= offset + len);
+
+               end = start + skb_shinfo(skb)->frags[i].size;
+               if ((copy = end - offset) > 0) {
+                       skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+                       if (copy > len)
+                               copy = len;
+                       sg_set_page(&sg[elt], frag->page, copy,
+                                       frag->page_offset+offset-start);
+                       elt++;
+                       if (!(len -= copy))
+                               return elt;
+                       offset += copy;
+               }
+               start = end;
+       }
+
+       if (skb_shinfo(skb)->frag_list) {
+               struct sk_buff *list = skb_shinfo(skb)->frag_list;
+
+               for (; list; list = list->next) {
+                       int end;
+
+                       BUG_TRAP(start <= offset + len);
+
+                       end = start + list->len;
+                       if ((copy = end - offset) > 0) {
+                               if (copy > len)
+                                       copy = len;
+                               elt += __skb_to_sgvec(list, sg+elt, offset - start,
+                                                     copy);
+                               if ((len -= copy) == 0)
+                                       return elt;
+                               offset += copy;
+                       }
+                       start = end;
+               }
+       }
+       BUG_ON(len);
+       return elt;
+}
+
+int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len)
+{
+       int nsg = __skb_to_sgvec(skb, sg, offset, len);
+
+       sg_mark_end(&sg[nsg - 1]);
+
+       return nsg;
+}
+
+/**
+ *     skb_cow_data - Check that a socket buffer's data buffers are writable
+ *     @skb: The socket buffer to check.
+ *     @tailbits: Amount of trailing space to be added
+ *     @trailer: Returned pointer to the skb where the @tailbits space begins
+ *
+ *     Make sure that the data buffers attached to a socket buffer are
+ *     writable. If they are not, private copies are made of the data buffers
+ *     and the socket buffer is set to use these instead.
+ *
+ *     If @tailbits is given, make sure that there is space to write @tailbits
+ *     bytes of data beyond current end of socket buffer.  @trailer will be
+ *     set to point to the skb in which this space begins.
+ *
+ *     The number of scatterlist elements required to completely map the
+ *     COW'd and extended socket buffer will be returned.
+ */
+int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer)
+{
+       int copyflag;
+       int elt;
+       struct sk_buff *skb1, **skb_p;
+
+       /* If skb is cloned or its head is paged, reallocate
+        * head pulling out all the pages (pages are considered not writable
+        * at the moment even if they are anonymous).
+        */
+       if ((skb_cloned(skb) || skb_shinfo(skb)->nr_frags) &&
+           __pskb_pull_tail(skb, skb_pagelen(skb)-skb_headlen(skb)) == NULL)
+               return -ENOMEM;
+
+       /* Easy case. Most of packets will go this way. */
+       if (!skb_shinfo(skb)->frag_list) {
+               /* A little of trouble, not enough of space for trailer.
+                * This should not happen, when stack is tuned to generate
+                * good frames. OK, on miss we reallocate and reserve even more
+                * space, 128 bytes is fair. */
+
+               if (skb_tailroom(skb) < tailbits &&
+                   pskb_expand_head(skb, 0, tailbits-skb_tailroom(skb)+128, GFP_ATOMIC))
+                       return -ENOMEM;
+
+               /* Voila! */
+               *trailer = skb;
+               return 1;
+       }
+
+       /* Misery. We are in troubles, going to mincer fragments... */
+
+       elt = 1;
+       skb_p = &skb_shinfo(skb)->frag_list;
+       copyflag = 0;
+
+       while ((skb1 = *skb_p) != NULL) {
+               int ntail = 0;
+
+               /* The fragment is partially pulled by someone,
+                * this can happen on input. Copy it and everything
+                * after it. */
+
+               if (skb_shared(skb1))
+                       copyflag = 1;
+
+               /* If the skb is the last, worry about trailer. */
+
+               if (skb1->next == NULL && tailbits) {
+                       if (skb_shinfo(skb1)->nr_frags ||
+                           skb_shinfo(skb1)->frag_list ||
+                           skb_tailroom(skb1) < tailbits)
+                               ntail = tailbits + 128;
+               }
+
+               if (copyflag ||
+                   skb_cloned(skb1) ||
+                   ntail ||
+                   skb_shinfo(skb1)->nr_frags ||
+                   skb_shinfo(skb1)->frag_list) {
+                       struct sk_buff *skb2;
+
+                       /* Fuck, we are miserable poor guys... */
+                       if (ntail == 0)
+                               skb2 = skb_copy(skb1, GFP_ATOMIC);
+                       else
+                               skb2 = skb_copy_expand(skb1,
+                                                      skb_headroom(skb1),
+                                                      ntail,
+                                                      GFP_ATOMIC);
+                       if (unlikely(skb2 == NULL))
+                               return -ENOMEM;
+
+                       if (skb1->sk)
+                               skb_set_owner_w(skb2, skb1->sk);
+
+                       /* Looking around. Are we still alive?
+                        * OK, link new skb, drop old one */
+
+                       skb2->next = skb1->next;
+                       *skb_p = skb2;
+                       kfree_skb(skb1);
+                       skb1 = skb2;
+               }
+               elt++;
+               *trailer = skb1;
+               skb_p = &skb1->next;
+       }
+
+       return elt;
 }
 
 EXPORT_SYMBOL(___pskb_trim);
@@ -2002,7 +2471,6 @@ EXPORT_SYMBOL(pskb_copy);
 EXPORT_SYMBOL(pskb_expand_head);
 EXPORT_SYMBOL(skb_checksum);
 EXPORT_SYMBOL(skb_clone);
-EXPORT_SYMBOL(skb_clone_fraglist);
 EXPORT_SYMBOL(skb_copy);
 EXPORT_SYMBOL(skb_copy_and_csum_bits);
 EXPORT_SYMBOL(skb_copy_and_csum_dev);
@@ -2026,3 +2494,6 @@ EXPORT_SYMBOL(skb_seq_read);
 EXPORT_SYMBOL(skb_abort_seq_read);
 EXPORT_SYMBOL(skb_find_text);
 EXPORT_SYMBOL(skb_append_datato_frags);
+
+EXPORT_SYMBOL_GPL(skb_to_sgvec);
+EXPORT_SYMBOL_GPL(skb_cow_data);