diff options
author | Vlad Yasevich <vyasevich@gmail.com> | 2015-01-31 10:40:16 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-02-02 22:28:04 -0500 |
commit | d39d938c8228a4c5860138a53cf2b9ae4c4baec2 (patch) | |
tree | 187e996dd064f32b17528c357bf07149be77012b /net/ipv6/udp.c | |
parent | 6422398c2ab09268a55112f98cbf96bbf0184328 (diff) |
ipv6: Introduce udpv6_send_skb()
Now that we can individually construct IPv6 skbs to send, add a
udpv6_send_skb() function to populate the udp header and send the
skb. This allows udp_v6_push_pending_frames() to re-use this
function as well as enables us to add lockless sendmsg() support.
Signed-off-by: Vladislav Yasevich <vyasevic@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/udp.c')
-rw-r--r-- | net/ipv6/udp.c | 67 |
1 files changed, 40 insertions, 27 deletions
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index e41f017cd479..67a3d70f7ac4 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -990,9 +990,10 @@ static void udp6_hwcsum_outgoing(struct sock *sk, struct sk_buff *skb, | |||
990 | { | 990 | { |
991 | unsigned int offset; | 991 | unsigned int offset; |
992 | struct udphdr *uh = udp_hdr(skb); | 992 | struct udphdr *uh = udp_hdr(skb); |
993 | struct sk_buff *frags = skb_shinfo(skb)->frag_list; | ||
993 | __wsum csum = 0; | 994 | __wsum csum = 0; |
994 | 995 | ||
995 | if (skb_queue_len(&sk->sk_write_queue) == 1) { | 996 | if (!frags) { |
996 | /* Only one fragment on the socket. */ | 997 | /* Only one fragment on the socket. */ |
997 | skb->csum_start = skb_transport_header(skb) - skb->head; | 998 | skb->csum_start = skb_transport_header(skb) - skb->head; |
998 | skb->csum_offset = offsetof(struct udphdr, check); | 999 | skb->csum_offset = offsetof(struct udphdr, check); |
@@ -1008,9 +1009,9 @@ static void udp6_hwcsum_outgoing(struct sock *sk, struct sk_buff *skb, | |||
1008 | 1009 | ||
1009 | skb->ip_summed = CHECKSUM_NONE; | 1010 | skb->ip_summed = CHECKSUM_NONE; |
1010 | 1011 | ||
1011 | skb_queue_walk(&sk->sk_write_queue, skb) { | 1012 | do { |
1012 | csum = csum_add(csum, skb->csum); | 1013 | csum = csum_add(csum, frags->csum); |
1013 | } | 1014 | } while ((frags = frags->next)); |
1014 | 1015 | ||
1015 | uh->check = csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, | 1016 | uh->check = csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, |
1016 | csum); | 1017 | csum); |
@@ -1023,26 +1024,15 @@ static void udp6_hwcsum_outgoing(struct sock *sk, struct sk_buff *skb, | |||
1023 | * Sending | 1024 | * Sending |
1024 | */ | 1025 | */ |
1025 | 1026 | ||
1026 | static int udp_v6_push_pending_frames(struct sock *sk) | 1027 | static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6) |
1027 | { | 1028 | { |
1028 | struct sk_buff *skb; | 1029 | struct sock *sk = skb->sk; |
1029 | struct udphdr *uh; | 1030 | struct udphdr *uh; |
1030 | struct udp_sock *up = udp_sk(sk); | ||
1031 | struct inet_sock *inet = inet_sk(sk); | ||
1032 | struct flowi6 *fl6; | ||
1033 | int err = 0; | 1031 | int err = 0; |
1034 | int is_udplite = IS_UDPLITE(sk); | 1032 | int is_udplite = IS_UDPLITE(sk); |
1035 | __wsum csum = 0; | 1033 | __wsum csum = 0; |
1036 | 1034 | int offset = skb_transport_offset(skb); | |
1037 | if (up->pending == AF_INET) | 1035 | int len = skb->len - offset; |
1038 | return udp_push_pending_frames(sk); | ||
1039 | |||
1040 | fl6 = &inet->cork.fl.u.ip6; | ||
1041 | |||
1042 | /* Grab the skbuff where UDP header space exists. */ | ||
1043 | skb = skb_peek(&sk->sk_write_queue); | ||
1044 | if (skb == NULL) | ||
1045 | goto out; | ||
1046 | 1036 | ||
1047 | /* | 1037 | /* |
1048 | * Create a UDP header | 1038 | * Create a UDP header |
@@ -1050,29 +1040,28 @@ static int udp_v6_push_pending_frames(struct sock *sk) | |||
1050 | uh = udp_hdr(skb); | 1040 | uh = udp_hdr(skb); |
1051 | uh->source = fl6->fl6_sport; | 1041 | uh->source = fl6->fl6_sport; |
1052 | uh->dest = fl6->fl6_dport; | 1042 | uh->dest = fl6->fl6_dport; |
1053 | uh->len = htons(up->len); | 1043 | uh->len = htons(len); |
1054 | uh->check = 0; | 1044 | uh->check = 0; |
1055 | 1045 | ||
1056 | if (is_udplite) | 1046 | if (is_udplite) |
1057 | csum = udplite_csum_outgoing(sk, skb); | 1047 | csum = udplite_csum(skb); |
1058 | else if (up->no_check6_tx) { /* UDP csum disabled */ | 1048 | else if (udp_sk(sk)->no_check6_tx) { /* UDP csum disabled */ |
1059 | skb->ip_summed = CHECKSUM_NONE; | 1049 | skb->ip_summed = CHECKSUM_NONE; |
1060 | goto send; | 1050 | goto send; |
1061 | } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ | 1051 | } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ |
1062 | udp6_hwcsum_outgoing(sk, skb, &fl6->saddr, &fl6->daddr, | 1052 | udp6_hwcsum_outgoing(sk, skb, &fl6->saddr, &fl6->daddr, len); |
1063 | up->len); | ||
1064 | goto send; | 1053 | goto send; |
1065 | } else | 1054 | } else |
1066 | csum = udp_csum_outgoing(sk, skb); | 1055 | csum = udp_csum(skb); |
1067 | 1056 | ||
1068 | /* add protocol-dependent pseudo-header */ | 1057 | /* add protocol-dependent pseudo-header */ |
1069 | uh->check = csum_ipv6_magic(&fl6->saddr, &fl6->daddr, | 1058 | uh->check = csum_ipv6_magic(&fl6->saddr, &fl6->daddr, |
1070 | up->len, fl6->flowi6_proto, csum); | 1059 | len, fl6->flowi6_proto, csum); |
1071 | if (uh->check == 0) | 1060 | if (uh->check == 0) |
1072 | uh->check = CSUM_MANGLED_0; | 1061 | uh->check = CSUM_MANGLED_0; |
1073 | 1062 | ||
1074 | send: | 1063 | send: |
1075 | err = ip6_push_pending_frames(sk); | 1064 | err = ip6_send_skb(skb); |
1076 | if (err) { | 1065 | if (err) { |
1077 | if (err == -ENOBUFS && !inet6_sk(sk)->recverr) { | 1066 | if (err == -ENOBUFS && !inet6_sk(sk)->recverr) { |
1078 | UDP6_INC_STATS_USER(sock_net(sk), | 1067 | UDP6_INC_STATS_USER(sock_net(sk), |
@@ -1082,6 +1071,30 @@ send: | |||
1082 | } else | 1071 | } else |
1083 | UDP6_INC_STATS_USER(sock_net(sk), | 1072 | UDP6_INC_STATS_USER(sock_net(sk), |
1084 | UDP_MIB_OUTDATAGRAMS, is_udplite); | 1073 | UDP_MIB_OUTDATAGRAMS, is_udplite); |
1074 | return err; | ||
1075 | } | ||
1076 | |||
1077 | static int udp_v6_push_pending_frames(struct sock *sk) | ||
1078 | { | ||
1079 | struct sk_buff *skb; | ||
1080 | struct udp_sock *up = udp_sk(sk); | ||
1081 | struct flowi6 fl6; | ||
1082 | int err = 0; | ||
1083 | |||
1084 | if (up->pending == AF_INET) | ||
1085 | return udp_push_pending_frames(sk); | ||
1086 | |||
1087 | /* ip6_finish_skb will release the cork, so make a copy of | ||
1088 | * fl6 here. | ||
1089 | */ | ||
1090 | fl6 = inet_sk(sk)->cork.fl.u.ip6; | ||
1091 | |||
1092 | skb = ip6_finish_skb(sk); | ||
1093 | if (!skb) | ||
1094 | goto out; | ||
1095 | |||
1096 | err = udp_v6_send_skb(skb, &fl6); | ||
1097 | |||
1085 | out: | 1098 | out: |
1086 | up->len = 0; | 1099 | up->len = 0; |
1087 | up->pending = 0; | 1100 | up->pending = 0; |