diff options
author | Thomas Graf <tgraf@suug.ch> | 2013-12-13 09:22:17 -0500 |
---|---|---|
committer | Jesse Gross <jesse@nicira.com> | 2014-01-06 18:52:42 -0500 |
commit | af2806f8f90a150160be898cd85332459c83c5cb (patch) | |
tree | d3476ee225083ad769e8d2374b840a4af0816560 | |
parent | 5f03f47c9c05086e181db3ec7a809f8454e28370 (diff) |
net: Export skb_zerocopy() to zerocopy from one skb to another
Make the skb zerocopy logic written for nfnetlink queue available for
use by other modules.
Signed-off-by: Thomas Graf <tgraf@suug.ch>
Reviewed-by: Daniel Borkmann <dborkman@redhat.com>
Acked-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Jesse Gross <jesse@nicira.com>
-rw-r--r-- | include/linux/skbuff.h | 3 | ||||
-rw-r--r-- | net/core/skbuff.c | 85 | ||||
-rw-r--r-- | net/netfilter/nfnetlink_queue_core.c | 59 |
3 files changed, 92 insertions, 55 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index bec1cc7d5e3c..7c48e2d4c72b 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h | |||
@@ -2345,6 +2345,9 @@ int skb_splice_bits(struct sk_buff *skb, unsigned int offset, | |||
2345 | struct pipe_inode_info *pipe, unsigned int len, | 2345 | struct pipe_inode_info *pipe, unsigned int len, |
2346 | unsigned int flags); | 2346 | unsigned int flags); |
2347 | void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to); | 2347 | void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to); |
2348 | unsigned int skb_zerocopy_headlen(const struct sk_buff *from); | ||
2349 | void skb_zerocopy(struct sk_buff *to, const struct sk_buff *from, | ||
2350 | int len, int hlen); | ||
2348 | void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len); | 2351 | void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len); |
2349 | int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen); | 2352 | int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen); |
2350 | void skb_scrub_packet(struct sk_buff *skb, bool xnet); | 2353 | void skb_scrub_packet(struct sk_buff *skb, bool xnet); |
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; |