aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/ip_output.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/ip_output.c')
-rw-r--r--net/ipv4/ip_output.c23
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
1110error_efault: 1130error_efault:
1111 err = -EFAULT; 1131 err = -EFAULT;
1112error: 1132error:
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);