aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Graf <tgraf@suug.ch>2013-12-13 09:22:17 -0500
committerJesse Gross <jesse@nicira.com>2014-01-06 18:52:42 -0500
commitaf2806f8f90a150160be898cd85332459c83c5cb (patch)
treed3476ee225083ad769e8d2374b840a4af0816560
parent5f03f47c9c05086e181db3ec7a809f8454e28370 (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.h3
-rw-r--r--net/core/skbuff.c85
-rw-r--r--net/netfilter/nfnetlink_queue_core.c59
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);
2347void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to); 2347void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to);
2348unsigned int skb_zerocopy_headlen(const struct sk_buff *from);
2349void skb_zerocopy(struct sk_buff *to, const struct sk_buff *from,
2350 int len, int hlen);
2348void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len); 2351void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len);
2349int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen); 2352int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen);
2350void skb_scrub_packet(struct sk_buff *skb, bool xnet); 2353void 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}
2123EXPORT_SYMBOL(skb_copy_and_csum_bits); 2123EXPORT_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 */
2132unsigned int
2133skb_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}
2147EXPORT_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 */
2162void
2163skb_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}
2208EXPORT_SYMBOL_GPL(skb_zerocopy);
2209
2125void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to) 2210void 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
238static void
239nfqnl_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
283static int 238static int
284nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet, 239nfqnl_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;