diff options
Diffstat (limited to 'net/core/skbuff.c')
-rw-r--r-- | net/core/skbuff.c | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 267185a848f6..844b8abeb18c 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c | |||
@@ -2018,6 +2018,146 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len) | |||
2018 | skb_split_no_header(skb, skb1, len, pos); | 2018 | skb_split_no_header(skb, skb1, len, pos); |
2019 | } | 2019 | } |
2020 | 2020 | ||
2021 | /* Shifting from/to a cloned skb is a no-go. | ||
2022 | * | ||
2023 | * TODO: handle cloned skbs by using pskb_expand_head() | ||
2024 | */ | ||
2025 | static int skb_prepare_for_shift(struct sk_buff *skb) | ||
2026 | { | ||
2027 | return skb_cloned(skb); | ||
2028 | } | ||
2029 | |||
2030 | /** | ||
2031 | * skb_shift - Shifts paged data partially from skb to another | ||
2032 | * @tgt: buffer into which tail data gets added | ||
2033 | * @skb: buffer from which the paged data comes from | ||
2034 | * @shiftlen: shift up to this many bytes | ||
2035 | * | ||
2036 | * Attempts to shift up to shiftlen worth of bytes, which may be less than | ||
2037 | * the length of the skb, from tgt to skb. Returns number bytes shifted. | ||
2038 | * It's up to caller to free skb if everything was shifted. | ||
2039 | * | ||
2040 | * If @tgt runs out of frags, the whole operation is aborted. | ||
2041 | * | ||
2042 | * Skb cannot include anything else but paged data while tgt is allowed | ||
2043 | * to have non-paged data as well. | ||
2044 | * | ||
2045 | * TODO: full sized shift could be optimized but that would need | ||
2046 | * specialized skb free'er to handle frags without up-to-date nr_frags. | ||
2047 | */ | ||
2048 | int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen) | ||
2049 | { | ||
2050 | int from, to, merge, todo; | ||
2051 | struct skb_frag_struct *fragfrom, *fragto; | ||
2052 | |||
2053 | BUG_ON(shiftlen > skb->len); | ||
2054 | BUG_ON(skb_headlen(skb)); /* Would corrupt stream */ | ||
2055 | |||
2056 | todo = shiftlen; | ||
2057 | from = 0; | ||
2058 | to = skb_shinfo(tgt)->nr_frags; | ||
2059 | fragfrom = &skb_shinfo(skb)->frags[from]; | ||
2060 | |||
2061 | /* Actual merge is delayed until the point when we know we can | ||
2062 | * commit all, so that we don't have to undo partial changes | ||
2063 | */ | ||
2064 | if (!to || | ||
2065 | !skb_can_coalesce(tgt, to, fragfrom->page, fragfrom->page_offset)) { | ||
2066 | merge = -1; | ||
2067 | } else { | ||
2068 | merge = to - 1; | ||
2069 | |||
2070 | todo -= fragfrom->size; | ||
2071 | if (todo < 0) { | ||
2072 | if (skb_prepare_for_shift(skb) || | ||
2073 | skb_prepare_for_shift(tgt)) | ||
2074 | return 0; | ||
2075 | |||
2076 | fragto = &skb_shinfo(tgt)->frags[merge]; | ||
2077 | |||
2078 | fragto->size += shiftlen; | ||
2079 | fragfrom->size -= shiftlen; | ||
2080 | fragfrom->page_offset += shiftlen; | ||
2081 | |||
2082 | goto onlymerged; | ||
2083 | } | ||
2084 | |||
2085 | from++; | ||
2086 | } | ||
2087 | |||
2088 | /* Skip full, not-fitting skb to avoid expensive operations */ | ||
2089 | if ((shiftlen == skb->len) && | ||
2090 | (skb_shinfo(skb)->nr_frags - from) > (MAX_SKB_FRAGS - to)) | ||
2091 | return 0; | ||
2092 | |||
2093 | if (skb_prepare_for_shift(skb) || skb_prepare_for_shift(tgt)) | ||
2094 | return 0; | ||
2095 | |||
2096 | while ((todo > 0) && (from < skb_shinfo(skb)->nr_frags)) { | ||
2097 | if (to == MAX_SKB_FRAGS) | ||
2098 | return 0; | ||
2099 | |||
2100 | fragfrom = &skb_shinfo(skb)->frags[from]; | ||
2101 | fragto = &skb_shinfo(tgt)->frags[to]; | ||
2102 | |||
2103 | if (todo >= fragfrom->size) { | ||
2104 | *fragto = *fragfrom; | ||
2105 | todo -= fragfrom->size; | ||
2106 | from++; | ||
2107 | to++; | ||
2108 | |||
2109 | } else { | ||
2110 | get_page(fragfrom->page); | ||
2111 | fragto->page = fragfrom->page; | ||
2112 | fragto->page_offset = fragfrom->page_offset; | ||
2113 | fragto->size = todo; | ||
2114 | |||
2115 | fragfrom->page_offset += todo; | ||
2116 | fragfrom->size -= todo; | ||
2117 | todo = 0; | ||
2118 | |||
2119 | to++; | ||
2120 | break; | ||
2121 | } | ||
2122 | } | ||
2123 | |||
2124 | /* Ready to "commit" this state change to tgt */ | ||
2125 | skb_shinfo(tgt)->nr_frags = to; | ||
2126 | |||
2127 | if (merge >= 0) { | ||
2128 | fragfrom = &skb_shinfo(skb)->frags[0]; | ||
2129 | fragto = &skb_shinfo(tgt)->frags[merge]; | ||
2130 | |||
2131 | fragto->size += fragfrom->size; | ||
2132 | put_page(fragfrom->page); | ||
2133 | } | ||
2134 | |||
2135 | /* Reposition in the original skb */ | ||
2136 | to = 0; | ||
2137 | while (from < skb_shinfo(skb)->nr_frags) | ||
2138 | skb_shinfo(skb)->frags[to++] = skb_shinfo(skb)->frags[from++]; | ||
2139 | skb_shinfo(skb)->nr_frags = to; | ||
2140 | |||
2141 | BUG_ON(todo > 0 && !skb_shinfo(skb)->nr_frags); | ||
2142 | |||
2143 | onlymerged: | ||
2144 | /* Most likely the tgt won't ever need its checksum anymore, skb on | ||
2145 | * the other hand might need it if it needs to be resent | ||
2146 | */ | ||
2147 | tgt->ip_summed = CHECKSUM_PARTIAL; | ||
2148 | skb->ip_summed = CHECKSUM_PARTIAL; | ||
2149 | |||
2150 | /* Yak, is it really working this way? Some helper please? */ | ||
2151 | skb->len -= shiftlen; | ||
2152 | skb->data_len -= shiftlen; | ||
2153 | skb->truesize -= shiftlen; | ||
2154 | tgt->len += shiftlen; | ||
2155 | tgt->data_len += shiftlen; | ||
2156 | tgt->truesize += shiftlen; | ||
2157 | |||
2158 | return shiftlen; | ||
2159 | } | ||
2160 | |||
2021 | /** | 2161 | /** |
2022 | * skb_prepare_seq_read - Prepare a sequential read of skb data | 2162 | * skb_prepare_seq_read - Prepare a sequential read of skb data |
2023 | * @skb: the buffer to read | 2163 | * @skb: the buffer to read |