Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6
[pandora-kernel.git] / net / core / skbuff.c
index 3666216..e411567 100644 (file)
@@ -4,8 +4,6 @@
  *     Authors:        Alan Cox <iiitac@pyr.swan.ac.uk>
  *                     Florian La Roche <rzsfl@rz.uni-sb.de>
  *
- *     Version:        $Id: skbuff.c,v 1.90 2001/11/07 05:56:19 davem Exp $
- *
  *     Fixes:
  *             Alan Cox        :       Fixed the worst of the load
  *                                     balancer bugs.
@@ -461,6 +459,8 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
        new->tc_verd            = old->tc_verd;
 #endif
 #endif
+       new->vlan_tci           = old->vlan_tci;
+
        skb_copy_secmark(new, old);
 }
 
@@ -1282,114 +1282,83 @@ static inline int spd_fill_page(struct splice_pipe_desc *spd, struct page *page,
        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, error = 0;
-
-       toff = *offset;
-       tlen = *total_len;
-       if (!tlen) {
-               error = 1;
-               goto err;
+static inline void __segment_seek(struct page **page, unsigned int *poff,
+                                 unsigned int *plen, unsigned int off)
+{
+       *poff += off;
+       *page += *poff / PAGE_SIZE;
+       *poff = *poff % PAGE_SIZE;
+       *plen -= off;
+}
+
+static inline int __splice_segment(struct page *page, unsigned int poff,
+                                  unsigned int plen, unsigned int *off,
+                                  unsigned int *len, struct sk_buff *skb,
+                                  struct splice_pipe_desc *spd)
+{
+       if (!*len)
+               return 1;
+
+       /* skip this segment if already processed */
+       if (*off >= plen) {
+               *off -= plen;
+               return 0;
        }
 
-       /*
-        * 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;
+       /* ignore any bits we already processed */
+       if (*off) {
+               __segment_seek(&page, &poff, &plen, *off);
+               *off = 0;
        }
 
-       /*
-        * 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;
-               }
+       do {
+               unsigned int flen = min(*len, plen);
 
-               plen = min(plen, tlen);
-               if (!plen)
-                       break;
+               /* the linear region may spread across several pages  */
+               flen = min_t(unsigned int, flen, PAGE_SIZE - poff);
 
-               /*
-                * just jump directly to update and return, no point
-                * in going over fragments when the output is full.
-                */
-               error = spd_fill_page(spd, virt_to_page(p), plen, poff, skb);
-               if (error)
-                       goto done;
+               if (spd_fill_page(spd, page, flen, poff, skb))
+                       return 1;
 
-               tlen -= plen;
-       }
+               __segment_seek(&page, &poff, &plen, flen);
+               *len -= flen;
+
+       } while (*len && plen);
+
+       return 0;
+}
+
+/*
+ * Map linear and fragment data from the skb to spd. It reports failure if the
+ * pipe is full or if we already spliced the requested length.
+ */
+static int __skb_splice_bits(struct sk_buff *skb, unsigned int *offset,
+                     unsigned int *len,
+                     struct splice_pipe_desc *spd)
+{
+       int seg;
+
+       /*
+        * map the linear part
+        */
+       if (__splice_segment(virt_to_page(skb->data),
+                            (unsigned long) skb->data & (PAGE_SIZE - 1),
+                            skb_headlen(skb),
+                            offset, len, skb, spd))
+               return 1;
 
        /*
         * 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;
-
-               error = spd_fill_page(spd, f->page, plen, poff, skb);
-               if (error)
-                       break;
-
-               tlen -= plen;
+               if (__splice_segment(f->page, f->page_offset, f->size,
+                                    offset, len, skb, spd))
+                       return 1;
        }
 
-done:
-       if (spd->nr_pages - nr_pages) {
-               *offset = 0;
-               *total_len = tlen;
-               return 0;
-       }
-err:
-       /* update the offset to reflect the linear part skip, if any */
-       if (!error)
-               *offset = toff;
-       return error;
+       return 0;
 }
 
 /*
@@ -2288,6 +2257,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features)
                skb_copy_queue_mapping(nskb, skb);
                nskb->priority = skb->priority;
                nskb->protocol = skb->protocol;
+               nskb->vlan_tci = skb->vlan_tci;
                nskb->dst = dst_clone(skb->dst);
                memcpy(nskb->cb, skb->cb, sizeof(skb->cb));
                nskb->pkt_type = skb->pkt_type;
@@ -2592,6 +2562,13 @@ bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off)
        return true;
 }
 
+void __skb_warn_lro_forwarding(const struct sk_buff *skb)
+{
+       if (net_ratelimit())
+               pr_warning("%s: received packets cannot be forwarded"
+                          " while LRO is enabled\n", skb->dev->name);
+}
+
 EXPORT_SYMBOL(___pskb_trim);
 EXPORT_SYMBOL(__kfree_skb);
 EXPORT_SYMBOL(kfree_skb);
@@ -2625,6 +2602,7 @@ EXPORT_SYMBOL(skb_seq_read);
 EXPORT_SYMBOL(skb_abort_seq_read);
 EXPORT_SYMBOL(skb_find_text);
 EXPORT_SYMBOL(skb_append_datato_frags);
+EXPORT_SYMBOL(__skb_warn_lro_forwarding);
 
 EXPORT_SYMBOL_GPL(skb_to_sgvec);
 EXPORT_SYMBOL_GPL(skb_cow_data);