diff options
Diffstat (limited to 'net/ipv4/ip_output.c')
-rw-r--r-- | net/ipv4/ip_output.c | 23 |
1 files changed, 22 insertions, 1 deletions
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 5dbec21856f4..6f843aff628c 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c | |||
@@ -867,6 +867,7 @@ static int __ip_append_data(struct sock *sk, | |||
867 | unsigned int flags) | 867 | unsigned int flags) |
868 | { | 868 | { |
869 | struct inet_sock *inet = inet_sk(sk); | 869 | struct inet_sock *inet = inet_sk(sk); |
870 | struct ubuf_info *uarg = NULL; | ||
870 | struct sk_buff *skb; | 871 | struct sk_buff *skb; |
871 | 872 | ||
872 | struct ip_options *opt = cork->opt; | 873 | struct ip_options *opt = cork->opt; |
@@ -916,6 +917,19 @@ static int __ip_append_data(struct sock *sk, | |||
916 | (!exthdrlen || (rt->dst.dev->features & NETIF_F_HW_ESP_TX_CSUM))) | 917 | (!exthdrlen || (rt->dst.dev->features & NETIF_F_HW_ESP_TX_CSUM))) |
917 | csummode = CHECKSUM_PARTIAL; | 918 | csummode = CHECKSUM_PARTIAL; |
918 | 919 | ||
920 | if (flags & MSG_ZEROCOPY && length && sock_flag(sk, SOCK_ZEROCOPY)) { | ||
921 | uarg = sock_zerocopy_realloc(sk, length, skb_zcopy(skb)); | ||
922 | if (!uarg) | ||
923 | return -ENOBUFS; | ||
924 | if (rt->dst.dev->features & NETIF_F_SG && | ||
925 | csummode == CHECKSUM_PARTIAL) { | ||
926 | paged = true; | ||
927 | } else { | ||
928 | uarg->zerocopy = 0; | ||
929 | skb_zcopy_set(skb, uarg); | ||
930 | } | ||
931 | } | ||
932 | |||
919 | cork->length += length; | 933 | cork->length += length; |
920 | 934 | ||
921 | /* So, what's going on in the loop below? | 935 | /* So, what's going on in the loop below? |
@@ -1006,6 +1020,7 @@ alloc_new_skb: | |||
1006 | cork->tx_flags = 0; | 1020 | cork->tx_flags = 0; |
1007 | skb_shinfo(skb)->tskey = tskey; | 1021 | skb_shinfo(skb)->tskey = tskey; |
1008 | tskey = 0; | 1022 | tskey = 0; |
1023 | skb_zcopy_set(skb, uarg); | ||
1009 | 1024 | ||
1010 | /* | 1025 | /* |
1011 | * Find where to start putting bytes. | 1026 | * Find where to start putting bytes. |
@@ -1068,7 +1083,7 @@ alloc_new_skb: | |||
1068 | err = -EFAULT; | 1083 | err = -EFAULT; |
1069 | goto error; | 1084 | goto error; |
1070 | } | 1085 | } |
1071 | } else { | 1086 | } else if (!uarg || !uarg->zerocopy) { |
1072 | int i = skb_shinfo(skb)->nr_frags; | 1087 | int i = skb_shinfo(skb)->nr_frags; |
1073 | 1088 | ||
1074 | err = -ENOMEM; | 1089 | err = -ENOMEM; |
@@ -1098,6 +1113,10 @@ alloc_new_skb: | |||
1098 | skb->data_len += copy; | 1113 | skb->data_len += copy; |
1099 | skb->truesize += copy; | 1114 | skb->truesize += copy; |
1100 | wmem_alloc_delta += copy; | 1115 | wmem_alloc_delta += copy; |
1116 | } else { | ||
1117 | err = skb_zerocopy_iter_dgram(skb, from, copy); | ||
1118 | if (err < 0) | ||
1119 | goto error; | ||
1101 | } | 1120 | } |
1102 | offset += copy; | 1121 | offset += copy; |
1103 | length -= copy; | 1122 | length -= copy; |
@@ -1105,11 +1124,13 @@ alloc_new_skb: | |||
1105 | 1124 | ||
1106 | if (wmem_alloc_delta) | 1125 | if (wmem_alloc_delta) |
1107 | refcount_add(wmem_alloc_delta, &sk->sk_wmem_alloc); | 1126 | refcount_add(wmem_alloc_delta, &sk->sk_wmem_alloc); |
1127 | sock_zerocopy_put(uarg); | ||
1108 | return 0; | 1128 | return 0; |
1109 | 1129 | ||
1110 | error_efault: | 1130 | error_efault: |
1111 | err = -EFAULT; | 1131 | err = -EFAULT; |
1112 | error: | 1132 | error: |
1133 | sock_zerocopy_put_abort(uarg); | ||
1113 | cork->length -= length; | 1134 | cork->length -= length; |
1114 | IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTDISCARDS); | 1135 | IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTDISCARDS); |
1115 | refcount_add(wmem_alloc_delta, &sk->sk_wmem_alloc); | 1136 | refcount_add(wmem_alloc_delta, &sk->sk_wmem_alloc); |