diff options
-rw-r--r-- | include/linux/skbuff.h | 4 | ||||
-rw-r--r-- | net/core/skbuff.c | 27 | ||||
-rw-r--r-- | net/netfilter/nfnetlink_queue_core.c | 9 | ||||
-rw-r--r-- | net/openvswitch/datapath.c | 6 |
4 files changed, 34 insertions, 12 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 5e1e6f2d98c2..15ede6a823a6 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h | |||
@@ -2451,8 +2451,8 @@ int skb_splice_bits(struct sk_buff *skb, unsigned int offset, | |||
2451 | unsigned int flags); | 2451 | unsigned int flags); |
2452 | void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to); | 2452 | void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to); |
2453 | unsigned int skb_zerocopy_headlen(const struct sk_buff *from); | 2453 | unsigned int skb_zerocopy_headlen(const struct sk_buff *from); |
2454 | void skb_zerocopy(struct sk_buff *to, const struct sk_buff *from, | 2454 | int skb_zerocopy(struct sk_buff *to, struct sk_buff *from, |
2455 | int len, int hlen); | 2455 | int len, int hlen); |
2456 | void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len); | 2456 | void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len); |
2457 | int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen); | 2457 | int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen); |
2458 | void skb_scrub_packet(struct sk_buff *skb, bool xnet); | 2458 | void skb_scrub_packet(struct sk_buff *skb, bool xnet); |
diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 869c7afe3b07..97e5a2c3d947 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c | |||
@@ -2127,25 +2127,31 @@ EXPORT_SYMBOL_GPL(skb_zerocopy_headlen); | |||
2127 | * | 2127 | * |
2128 | * The `hlen` as calculated by skb_zerocopy_headlen() specifies the | 2128 | * The `hlen` as calculated by skb_zerocopy_headlen() specifies the |
2129 | * headroom in the `to` buffer. | 2129 | * headroom in the `to` buffer. |
2130 | * | ||
2131 | * Return value: | ||
2132 | * 0: everything is OK | ||
2133 | * -ENOMEM: couldn't orphan frags of @from due to lack of memory | ||
2134 | * -EFAULT: skb_copy_bits() found some problem with skb geometry | ||
2130 | */ | 2135 | */ |
2131 | void | 2136 | int |
2132 | skb_zerocopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen) | 2137 | skb_zerocopy(struct sk_buff *to, struct sk_buff *from, int len, int hlen) |
2133 | { | 2138 | { |
2134 | int i, j = 0; | 2139 | int i, j = 0; |
2135 | int plen = 0; /* length of skb->head fragment */ | 2140 | int plen = 0; /* length of skb->head fragment */ |
2141 | int ret; | ||
2136 | struct page *page; | 2142 | struct page *page; |
2137 | unsigned int offset; | 2143 | unsigned int offset; |
2138 | 2144 | ||
2139 | BUG_ON(!from->head_frag && !hlen); | 2145 | BUG_ON(!from->head_frag && !hlen); |
2140 | 2146 | ||
2141 | /* dont bother with small payloads */ | 2147 | /* dont bother with small payloads */ |
2142 | if (len <= skb_tailroom(to)) { | 2148 | if (len <= skb_tailroom(to)) |
2143 | skb_copy_bits(from, 0, skb_put(to, len), len); | 2149 | return skb_copy_bits(from, 0, skb_put(to, len), len); |
2144 | return; | ||
2145 | } | ||
2146 | 2150 | ||
2147 | if (hlen) { | 2151 | if (hlen) { |
2148 | skb_copy_bits(from, 0, skb_put(to, hlen), hlen); | 2152 | ret = skb_copy_bits(from, 0, skb_put(to, hlen), hlen); |
2153 | if (unlikely(ret)) | ||
2154 | return ret; | ||
2149 | len -= hlen; | 2155 | len -= hlen; |
2150 | } else { | 2156 | } else { |
2151 | plen = min_t(int, skb_headlen(from), len); | 2157 | plen = min_t(int, skb_headlen(from), len); |
@@ -2163,6 +2169,11 @@ skb_zerocopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen) | |||
2163 | to->len += len + plen; | 2169 | to->len += len + plen; |
2164 | to->data_len += len + plen; | 2170 | to->data_len += len + plen; |
2165 | 2171 | ||
2172 | if (unlikely(skb_orphan_frags(from, GFP_ATOMIC))) { | ||
2173 | skb_tx_error(from); | ||
2174 | return -ENOMEM; | ||
2175 | } | ||
2176 | |||
2166 | for (i = 0; i < skb_shinfo(from)->nr_frags; i++) { | 2177 | for (i = 0; i < skb_shinfo(from)->nr_frags; i++) { |
2167 | if (!len) | 2178 | if (!len) |
2168 | break; | 2179 | break; |
@@ -2173,6 +2184,8 @@ skb_zerocopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen) | |||
2173 | j++; | 2184 | j++; |
2174 | } | 2185 | } |
2175 | skb_shinfo(to)->nr_frags = j; | 2186 | skb_shinfo(to)->nr_frags = j; |
2187 | |||
2188 | return 0; | ||
2176 | } | 2189 | } |
2177 | EXPORT_SYMBOL_GPL(skb_zerocopy); | 2190 | EXPORT_SYMBOL_GPL(skb_zerocopy); |
2178 | 2191 | ||
diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index f072fe803510..108120f216b1 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c | |||
@@ -354,13 +354,16 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, | |||
354 | 354 | ||
355 | skb = nfnetlink_alloc_skb(net, size, queue->peer_portid, | 355 | skb = nfnetlink_alloc_skb(net, size, queue->peer_portid, |
356 | GFP_ATOMIC); | 356 | GFP_ATOMIC); |
357 | if (!skb) | 357 | if (!skb) { |
358 | skb_tx_error(entskb); | ||
358 | return NULL; | 359 | return NULL; |
360 | } | ||
359 | 361 | ||
360 | nlh = nlmsg_put(skb, 0, 0, | 362 | nlh = nlmsg_put(skb, 0, 0, |
361 | NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET, | 363 | NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET, |
362 | sizeof(struct nfgenmsg), 0); | 364 | sizeof(struct nfgenmsg), 0); |
363 | if (!nlh) { | 365 | if (!nlh) { |
366 | skb_tx_error(entskb); | ||
364 | kfree_skb(skb); | 367 | kfree_skb(skb); |
365 | return NULL; | 368 | return NULL; |
366 | } | 369 | } |
@@ -488,13 +491,15 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, | |||
488 | nla->nla_type = NFQA_PAYLOAD; | 491 | nla->nla_type = NFQA_PAYLOAD; |
489 | nla->nla_len = nla_attr_size(data_len); | 492 | nla->nla_len = nla_attr_size(data_len); |
490 | 493 | ||
491 | skb_zerocopy(skb, entskb, data_len, hlen); | 494 | if (skb_zerocopy(skb, entskb, data_len, hlen)) |
495 | goto nla_put_failure; | ||
492 | } | 496 | } |
493 | 497 | ||
494 | nlh->nlmsg_len = skb->len; | 498 | nlh->nlmsg_len = skb->len; |
495 | return skb; | 499 | return skb; |
496 | 500 | ||
497 | nla_put_failure: | 501 | nla_put_failure: |
502 | skb_tx_error(entskb); | ||
498 | kfree_skb(skb); | 503 | kfree_skb(skb); |
499 | net_err_ratelimited("nf_queue: error creating packet message\n"); | 504 | net_err_ratelimited("nf_queue: error creating packet message\n"); |
500 | return NULL; | 505 | return NULL; |
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 8601b320b443..270b77dfac30 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c | |||
@@ -464,7 +464,9 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, | |||
464 | } | 464 | } |
465 | nla->nla_len = nla_attr_size(skb->len); | 465 | nla->nla_len = nla_attr_size(skb->len); |
466 | 466 | ||
467 | skb_zerocopy(user_skb, skb, skb->len, hlen); | 467 | err = skb_zerocopy(user_skb, skb, skb->len, hlen); |
468 | if (err) | ||
469 | goto out; | ||
468 | 470 | ||
469 | /* Pad OVS_PACKET_ATTR_PACKET if linear copy was performed */ | 471 | /* Pad OVS_PACKET_ATTR_PACKET if linear copy was performed */ |
470 | if (!(dp->user_features & OVS_DP_F_UNALIGNED)) { | 472 | if (!(dp->user_features & OVS_DP_F_UNALIGNED)) { |
@@ -478,6 +480,8 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, | |||
478 | 480 | ||
479 | err = genlmsg_unicast(ovs_dp_get_net(dp), user_skb, upcall_info->portid); | 481 | err = genlmsg_unicast(ovs_dp_get_net(dp), user_skb, upcall_info->portid); |
480 | out: | 482 | out: |
483 | if (err) | ||
484 | skb_tx_error(skb); | ||
481 | kfree_skb(nskb); | 485 | kfree_skb(nskb); |
482 | return err; | 486 | return err; |
483 | } | 487 | } |