diff options
Diffstat (limited to 'net')
| -rw-r--r-- | net/core/skbuff.c | 85 | ||||
| -rw-r--r-- | net/netfilter/nfnetlink_queue_core.c | 59 |
2 files changed, 89 insertions, 55 deletions
diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 2718fed53d8c..55859cb8b83d 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c | |||
| @@ -2122,6 +2122,91 @@ __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, | |||
| 2122 | } | 2122 | } |
| 2123 | EXPORT_SYMBOL(skb_copy_and_csum_bits); | 2123 | EXPORT_SYMBOL(skb_copy_and_csum_bits); |
| 2124 | 2124 | ||
| 2125 | /** | ||
| 2126 | * skb_zerocopy_headlen - Calculate headroom needed for skb_zerocopy() | ||
| 2127 | * @from: source buffer | ||
| 2128 | * | ||
| 2129 | * Calculates the amount of linear headroom needed in the 'to' skb passed | ||
| 2130 | * into skb_zerocopy(). | ||
| 2131 | */ | ||
| 2132 | unsigned int | ||
| 2133 | skb_zerocopy_headlen(const struct sk_buff *from) | ||
| 2134 | { | ||
| 2135 | unsigned int hlen = 0; | ||
| 2136 | |||
| 2137 | if (!from->head_frag || | ||
| 2138 | skb_headlen(from) < L1_CACHE_BYTES || | ||
| 2139 | skb_shinfo(from)->nr_frags >= MAX_SKB_FRAGS) | ||
| 2140 | hlen = skb_headlen(from); | ||
| 2141 | |||
| 2142 | if (skb_has_frag_list(from)) | ||
| 2143 | hlen = from->len; | ||
| 2144 | |||
| 2145 | return hlen; | ||
| 2146 | } | ||
| 2147 | EXPORT_SYMBOL_GPL(skb_zerocopy_headlen); | ||
| 2148 | |||
| 2149 | /** | ||
| 2150 | * skb_zerocopy - Zero copy skb to skb | ||
| 2151 | * @to: destination buffer | ||
| 2152 | * @source: source buffer | ||
| 2153 | * @len: number of bytes to copy from source buffer | ||
| 2154 | * @hlen: size of linear headroom in destination buffer | ||
| 2155 | * | ||
| 2156 | * Copies up to `len` bytes from `from` to `to` by creating references | ||
| 2157 | * to the frags in the source buffer. | ||
| 2158 | * | ||
| 2159 | * The `hlen` as calculated by skb_zerocopy_headlen() specifies the | ||
| 2160 | * headroom in the `to` buffer. | ||
| 2161 | */ | ||
| 2162 | void | ||
| 2163 | skb_zerocopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen) | ||
| 2164 | { | ||
| 2165 | int i, j = 0; | ||
| 2166 | int plen = 0; /* length of skb->head fragment */ | ||
| 2167 | struct page *page; | ||
| 2168 | unsigned int offset; | ||
| 2169 | |||
| 2170 | BUG_ON(!from->head_frag && !hlen); | ||
| 2171 | |||
| 2172 | /* dont bother with small payloads */ | ||
| 2173 | if (len <= skb_tailroom(to)) { | ||
| 2174 | skb_copy_bits(from, 0, skb_put(to, len), len); | ||
| 2175 | return; | ||
| 2176 | } | ||
| 2177 | |||
| 2178 | if (hlen) { | ||
| 2179 | skb_copy_bits(from, 0, skb_put(to, hlen), hlen); | ||
| 2180 | len -= hlen; | ||
| 2181 | } else { | ||
| 2182 | plen = min_t(int, skb_headlen(from), len); | ||
| 2183 | if (plen) { | ||
| 2184 | page = virt_to_head_page(from->head); | ||
| 2185 | offset = from->data - (unsigned char *)page_address(page); | ||
| 2186 | __skb_fill_page_desc(to, 0, page, offset, plen); | ||
| 2187 | get_page(page); | ||
| 2188 | j = 1; | ||
| 2189 | len -= plen; | ||
| 2190 | } | ||
| 2191 | } | ||
| 2192 | |||
| 2193 | to->truesize += len + plen; | ||
| 2194 | to->len += len + plen; | ||
| 2195 | to->data_len += len + plen; | ||
| 2196 | |||
| 2197 | for (i = 0; i < skb_shinfo(from)->nr_frags; i++) { | ||
| 2198 | if (!len) | ||
| 2199 | break; | ||
| 2200 | skb_shinfo(to)->frags[j] = skb_shinfo(from)->frags[i]; | ||
| 2201 | skb_shinfo(to)->frags[j].size = min_t(int, skb_shinfo(to)->frags[j].size, len); | ||
| 2202 | len -= skb_shinfo(to)->frags[j].size; | ||
| 2203 | skb_frag_ref(to, j); | ||
| 2204 | j++; | ||
| 2205 | } | ||
| 2206 | skb_shinfo(to)->nr_frags = j; | ||
| 2207 | } | ||
| 2208 | EXPORT_SYMBOL_GPL(skb_zerocopy); | ||
| 2209 | |||
| 2125 | void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to) | 2210 | void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to) |
| 2126 | { | 2211 | { |
| 2127 | __wsum csum; | 2212 | __wsum csum; |
diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index 21258cf70091..615ee12647ae 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c | |||
| @@ -235,51 +235,6 @@ nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data) | |||
| 235 | spin_unlock_bh(&queue->lock); | 235 | spin_unlock_bh(&queue->lock); |
| 236 | } | 236 | } |
| 237 | 237 | ||
| 238 | static void | ||
| 239 | nfqnl_zcopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen) | ||
| 240 | { | ||
| 241 | int i, j = 0; | ||
| 242 | int plen = 0; /* length of skb->head fragment */ | ||
| 243 | struct page *page; | ||
| 244 | unsigned int offset; | ||
| 245 | |||
| 246 | /* dont bother with small payloads */ | ||
| 247 | if (len <= skb_tailroom(to)) { | ||
| 248 | skb_copy_bits(from, 0, skb_put(to, len), len); | ||
| 249 | return; | ||
| 250 | } | ||
| 251 | |||
| 252 | if (hlen) { | ||
| 253 | skb_copy_bits(from, 0, skb_put(to, hlen), hlen); | ||
| 254 | len -= hlen; | ||
| 255 | } else { | ||
| 256 | plen = min_t(int, skb_headlen(from), len); | ||
| 257 | if (plen) { | ||
| 258 | page = virt_to_head_page(from->head); | ||
| 259 | offset = from->data - (unsigned char *)page_address(page); | ||
| 260 | __skb_fill_page_desc(to, 0, page, offset, plen); | ||
| 261 | get_page(page); | ||
| 262 | j = 1; | ||
| 263 | len -= plen; | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | to->truesize += len + plen; | ||
| 268 | to->len += len + plen; | ||
| 269 | to->data_len += len + plen; | ||
| 270 | |||
| 271 | for (i = 0; i < skb_shinfo(from)->nr_frags; i++) { | ||
| 272 | if (!len) | ||
| 273 | break; | ||
| 274 | skb_shinfo(to)->frags[j] = skb_shinfo(from)->frags[i]; | ||
| 275 | skb_shinfo(to)->frags[j].size = min_t(int, skb_shinfo(to)->frags[j].size, len); | ||
| 276 | len -= skb_shinfo(to)->frags[j].size; | ||
| 277 | skb_frag_ref(to, j); | ||
| 278 | j++; | ||
| 279 | } | ||
| 280 | skb_shinfo(to)->nr_frags = j; | ||
| 281 | } | ||
| 282 | |||
| 283 | static int | 238 | static int |
| 284 | nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet, | 239 | nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet, |
| 285 | bool csum_verify) | 240 | bool csum_verify) |
| @@ -304,7 +259,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, | |||
| 304 | { | 259 | { |
| 305 | size_t size; | 260 | size_t size; |
| 306 | size_t data_len = 0, cap_len = 0; | 261 | size_t data_len = 0, cap_len = 0; |
| 307 | int hlen = 0; | 262 | unsigned int hlen = 0; |
| 308 | struct sk_buff *skb; | 263 | struct sk_buff *skb; |
| 309 | struct nlattr *nla; | 264 | struct nlattr *nla; |
| 310 | struct nfqnl_msg_packet_hdr *pmsg; | 265 | struct nfqnl_msg_packet_hdr *pmsg; |
| @@ -356,14 +311,8 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, | |||
| 356 | if (data_len > entskb->len) | 311 | if (data_len > entskb->len) |
| 357 | data_len = entskb->len; | 312 | data_len = entskb->len; |
| 358 | 313 | ||
| 359 | if (!entskb->head_frag || | 314 | hlen = skb_zerocopy_headlen(entskb); |
| 360 | skb_headlen(entskb) < L1_CACHE_BYTES || | 315 | hlen = min_t(unsigned int, hlen, data_len); |
| 361 | skb_shinfo(entskb)->nr_frags >= MAX_SKB_FRAGS) | ||
| 362 | hlen = skb_headlen(entskb); | ||
| 363 | |||
| 364 | if (skb_has_frag_list(entskb)) | ||
| 365 | hlen = entskb->len; | ||
| 366 | hlen = min_t(int, data_len, hlen); | ||
| 367 | size += sizeof(struct nlattr) + hlen; | 316 | size += sizeof(struct nlattr) + hlen; |
| 368 | cap_len = entskb->len; | 317 | cap_len = entskb->len; |
| 369 | break; | 318 | break; |
| @@ -504,7 +453,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, | |||
| 504 | nla->nla_type = NFQA_PAYLOAD; | 453 | nla->nla_type = NFQA_PAYLOAD; |
| 505 | nla->nla_len = nla_attr_size(data_len); | 454 | nla->nla_len = nla_attr_size(data_len); |
| 506 | 455 | ||
| 507 | nfqnl_zcopy(skb, entskb, data_len, hlen); | 456 | skb_zerocopy(skb, entskb, data_len, hlen); |
| 508 | } | 457 | } |
| 509 | 458 | ||
| 510 | nlh->nlmsg_len = skb->len; | 459 | nlh->nlmsg_len = skb->len; |
