diff options
Diffstat (limited to 'net/core/skbuff.c')
-rw-r--r-- | net/core/skbuff.c | 178 |
1 files changed, 161 insertions, 17 deletions
diff --git a/net/core/skbuff.c b/net/core/skbuff.c index bb7210f4005e..8e5044ba3ab6 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c | |||
@@ -172,9 +172,9 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, | |||
172 | shinfo = skb_shinfo(skb); | 172 | shinfo = skb_shinfo(skb); |
173 | atomic_set(&shinfo->dataref, 1); | 173 | atomic_set(&shinfo->dataref, 1); |
174 | shinfo->nr_frags = 0; | 174 | shinfo->nr_frags = 0; |
175 | shinfo->tso_size = 0; | 175 | shinfo->gso_size = 0; |
176 | shinfo->tso_segs = 0; | 176 | shinfo->gso_segs = 0; |
177 | shinfo->ufo_size = 0; | 177 | shinfo->gso_type = 0; |
178 | shinfo->ip6_frag_id = 0; | 178 | shinfo->ip6_frag_id = 0; |
179 | shinfo->frag_list = NULL; | 179 | shinfo->frag_list = NULL; |
180 | 180 | ||
@@ -238,8 +238,9 @@ struct sk_buff *alloc_skb_from_cache(kmem_cache_t *cp, | |||
238 | 238 | ||
239 | atomic_set(&(skb_shinfo(skb)->dataref), 1); | 239 | atomic_set(&(skb_shinfo(skb)->dataref), 1); |
240 | skb_shinfo(skb)->nr_frags = 0; | 240 | skb_shinfo(skb)->nr_frags = 0; |
241 | skb_shinfo(skb)->tso_size = 0; | 241 | skb_shinfo(skb)->gso_size = 0; |
242 | skb_shinfo(skb)->tso_segs = 0; | 242 | skb_shinfo(skb)->gso_segs = 0; |
243 | skb_shinfo(skb)->gso_type = 0; | ||
243 | skb_shinfo(skb)->frag_list = NULL; | 244 | skb_shinfo(skb)->frag_list = NULL; |
244 | out: | 245 | out: |
245 | return skb; | 246 | return skb; |
@@ -528,8 +529,9 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) | |||
528 | #endif | 529 | #endif |
529 | skb_copy_secmark(new, old); | 530 | skb_copy_secmark(new, old); |
530 | atomic_set(&new->users, 1); | 531 | atomic_set(&new->users, 1); |
531 | skb_shinfo(new)->tso_size = skb_shinfo(old)->tso_size; | 532 | skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size; |
532 | skb_shinfo(new)->tso_segs = skb_shinfo(old)->tso_segs; | 533 | skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs; |
534 | skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type; | ||
533 | } | 535 | } |
534 | 536 | ||
535 | /** | 537 | /** |
@@ -781,24 +783,40 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb, | |||
781 | * filled. Used by network drivers which may DMA or transfer data | 783 | * filled. Used by network drivers which may DMA or transfer data |
782 | * beyond the buffer end onto the wire. | 784 | * beyond the buffer end onto the wire. |
783 | * | 785 | * |
784 | * May return NULL in out of memory cases. | 786 | * May return error in out of memory cases. The skb is freed on error. |
785 | */ | 787 | */ |
786 | 788 | ||
787 | struct sk_buff *skb_pad(struct sk_buff *skb, int pad) | 789 | int skb_pad(struct sk_buff *skb, int pad) |
788 | { | 790 | { |
789 | struct sk_buff *nskb; | 791 | int err; |
792 | int ntail; | ||
790 | 793 | ||
791 | /* If the skbuff is non linear tailroom is always zero.. */ | 794 | /* If the skbuff is non linear tailroom is always zero.. */ |
792 | if (skb_tailroom(skb) >= pad) { | 795 | if (!skb_cloned(skb) && skb_tailroom(skb) >= pad) { |
793 | memset(skb->data+skb->len, 0, pad); | 796 | memset(skb->data+skb->len, 0, pad); |
794 | return skb; | 797 | return 0; |
795 | } | 798 | } |
796 | 799 | ||
797 | nskb = skb_copy_expand(skb, skb_headroom(skb), skb_tailroom(skb) + pad, GFP_ATOMIC); | 800 | ntail = skb->data_len + pad - (skb->end - skb->tail); |
801 | if (likely(skb_cloned(skb) || ntail > 0)) { | ||
802 | err = pskb_expand_head(skb, 0, ntail, GFP_ATOMIC); | ||
803 | if (unlikely(err)) | ||
804 | goto free_skb; | ||
805 | } | ||
806 | |||
807 | /* FIXME: The use of this function with non-linear skb's really needs | ||
808 | * to be audited. | ||
809 | */ | ||
810 | err = skb_linearize(skb); | ||
811 | if (unlikely(err)) | ||
812 | goto free_skb; | ||
813 | |||
814 | memset(skb->data + skb->len, 0, pad); | ||
815 | return 0; | ||
816 | |||
817 | free_skb: | ||
798 | kfree_skb(skb); | 818 | kfree_skb(skb); |
799 | if (nskb) | 819 | return err; |
800 | memset(nskb->data+nskb->len, 0, pad); | ||
801 | return nskb; | ||
802 | } | 820 | } |
803 | 821 | ||
804 | /* Trims skb to length len. It can change skb pointers. | 822 | /* Trims skb to length len. It can change skb pointers. |
@@ -1824,6 +1842,132 @@ unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len) | |||
1824 | 1842 | ||
1825 | EXPORT_SYMBOL_GPL(skb_pull_rcsum); | 1843 | EXPORT_SYMBOL_GPL(skb_pull_rcsum); |
1826 | 1844 | ||
1845 | /** | ||
1846 | * skb_segment - Perform protocol segmentation on skb. | ||
1847 | * @skb: buffer to segment | ||
1848 | * @sg: whether scatter-gather can be used for generated segments | ||
1849 | * | ||
1850 | * This function performs segmentation on the given skb. It returns | ||
1851 | * the segment at the given position. It returns NULL if there are | ||
1852 | * no more segments to generate, or when an error is encountered. | ||
1853 | */ | ||
1854 | struct sk_buff *skb_segment(struct sk_buff *skb, int sg) | ||
1855 | { | ||
1856 | struct sk_buff *segs = NULL; | ||
1857 | struct sk_buff *tail = NULL; | ||
1858 | unsigned int mss = skb_shinfo(skb)->gso_size; | ||
1859 | unsigned int doffset = skb->data - skb->mac.raw; | ||
1860 | unsigned int offset = doffset; | ||
1861 | unsigned int headroom; | ||
1862 | unsigned int len; | ||
1863 | int nfrags = skb_shinfo(skb)->nr_frags; | ||
1864 | int err = -ENOMEM; | ||
1865 | int i = 0; | ||
1866 | int pos; | ||
1867 | |||
1868 | __skb_push(skb, doffset); | ||
1869 | headroom = skb_headroom(skb); | ||
1870 | pos = skb_headlen(skb); | ||
1871 | |||
1872 | do { | ||
1873 | struct sk_buff *nskb; | ||
1874 | skb_frag_t *frag; | ||
1875 | int hsize, nsize; | ||
1876 | int k; | ||
1877 | int size; | ||
1878 | |||
1879 | len = skb->len - offset; | ||
1880 | if (len > mss) | ||
1881 | len = mss; | ||
1882 | |||
1883 | hsize = skb_headlen(skb) - offset; | ||
1884 | if (hsize < 0) | ||
1885 | hsize = 0; | ||
1886 | nsize = hsize + doffset; | ||
1887 | if (nsize > len + doffset || !sg) | ||
1888 | nsize = len + doffset; | ||
1889 | |||
1890 | nskb = alloc_skb(nsize + headroom, GFP_ATOMIC); | ||
1891 | if (unlikely(!nskb)) | ||
1892 | goto err; | ||
1893 | |||
1894 | if (segs) | ||
1895 | tail->next = nskb; | ||
1896 | else | ||
1897 | segs = nskb; | ||
1898 | tail = nskb; | ||
1899 | |||
1900 | nskb->dev = skb->dev; | ||
1901 | nskb->priority = skb->priority; | ||
1902 | nskb->protocol = skb->protocol; | ||
1903 | nskb->dst = dst_clone(skb->dst); | ||
1904 | memcpy(nskb->cb, skb->cb, sizeof(skb->cb)); | ||
1905 | nskb->pkt_type = skb->pkt_type; | ||
1906 | nskb->mac_len = skb->mac_len; | ||
1907 | |||
1908 | skb_reserve(nskb, headroom); | ||
1909 | nskb->mac.raw = nskb->data; | ||
1910 | nskb->nh.raw = nskb->data + skb->mac_len; | ||
1911 | nskb->h.raw = nskb->nh.raw + (skb->h.raw - skb->nh.raw); | ||
1912 | memcpy(skb_put(nskb, doffset), skb->data, doffset); | ||
1913 | |||
1914 | if (!sg) { | ||
1915 | nskb->csum = skb_copy_and_csum_bits(skb, offset, | ||
1916 | skb_put(nskb, len), | ||
1917 | len, 0); | ||
1918 | continue; | ||
1919 | } | ||
1920 | |||
1921 | frag = skb_shinfo(nskb)->frags; | ||
1922 | k = 0; | ||
1923 | |||
1924 | nskb->ip_summed = CHECKSUM_HW; | ||
1925 | nskb->csum = skb->csum; | ||
1926 | memcpy(skb_put(nskb, hsize), skb->data + offset, hsize); | ||
1927 | |||
1928 | while (pos < offset + len) { | ||
1929 | BUG_ON(i >= nfrags); | ||
1930 | |||
1931 | *frag = skb_shinfo(skb)->frags[i]; | ||
1932 | get_page(frag->page); | ||
1933 | size = frag->size; | ||
1934 | |||
1935 | if (pos < offset) { | ||
1936 | frag->page_offset += offset - pos; | ||
1937 | frag->size -= offset - pos; | ||
1938 | } | ||
1939 | |||
1940 | k++; | ||
1941 | |||
1942 | if (pos + size <= offset + len) { | ||
1943 | i++; | ||
1944 | pos += size; | ||
1945 | } else { | ||
1946 | frag->size -= pos + size - (offset + len); | ||
1947 | break; | ||
1948 | } | ||
1949 | |||
1950 | frag++; | ||
1951 | } | ||
1952 | |||
1953 | skb_shinfo(nskb)->nr_frags = k; | ||
1954 | nskb->data_len = len - hsize; | ||
1955 | nskb->len += nskb->data_len; | ||
1956 | nskb->truesize += nskb->data_len; | ||
1957 | } while ((offset += len) < skb->len); | ||
1958 | |||
1959 | return segs; | ||
1960 | |||
1961 | err: | ||
1962 | while ((skb = segs)) { | ||
1963 | segs = skb->next; | ||
1964 | kfree(skb); | ||
1965 | } | ||
1966 | return ERR_PTR(err); | ||
1967 | } | ||
1968 | |||
1969 | EXPORT_SYMBOL_GPL(skb_segment); | ||
1970 | |||
1827 | void __init skb_init(void) | 1971 | void __init skb_init(void) |
1828 | { | 1972 | { |
1829 | skbuff_head_cache = kmem_cache_create("skbuff_head_cache", | 1973 | skbuff_head_cache = kmem_cache_create("skbuff_head_cache", |