diff options
author | Tom Herbert <therbert@google.com> | 2014-08-31 18:12:43 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-09-02 00:36:28 -0400 |
commit | 2abb7cdc0dc84e99b76ef983a1ae1978922aa9b3 (patch) | |
tree | 9bf6029678d74ef6ad12e832d48d769caf88a4ed | |
parent | d96535a17dbbafd567961d14c08c0984ddda9c3c (diff) |
udp: Add support for doing checksum unnecessary conversion
Add support for doing CHECKSUM_UNNECESSARY to CHECKSUM_COMPLETE
conversion in UDP tunneling path.
In the normal UDP path, we call skb_checksum_try_convert after locating
the UDP socket. The check is that checksum conversion is enabled for
the socket (new flag in UDP socket) and that checksum field is
non-zero.
In the UDP GRO path, we call skb_gro_checksum_try_convert after
checksum is validated and checksum field is non-zero. Since this is
already in GRO we assume that checksum conversion is always wanted.
Signed-off-by: Tom Herbert <therbert@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/udp.h | 16 | ||||
-rw-r--r-- | net/ipv4/udp.c | 4 | ||||
-rw-r--r-- | net/ipv4/udp_offload.c | 25 | ||||
-rw-r--r-- | net/ipv6/udp.c | 4 | ||||
-rw-r--r-- | net/ipv6/udp_offload.c | 24 |
5 files changed, 57 insertions, 16 deletions
diff --git a/include/linux/udp.h b/include/linux/udp.h index 247cfdcc4b08..ee3277593222 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h | |||
@@ -49,7 +49,11 @@ struct udp_sock { | |||
49 | unsigned int corkflag; /* Cork is required */ | 49 | unsigned int corkflag; /* Cork is required */ |
50 | __u8 encap_type; /* Is this an Encapsulation socket? */ | 50 | __u8 encap_type; /* Is this an Encapsulation socket? */ |
51 | unsigned char no_check6_tx:1,/* Send zero UDP6 checksums on TX? */ | 51 | unsigned char no_check6_tx:1,/* Send zero UDP6 checksums on TX? */ |
52 | no_check6_rx:1;/* Allow zero UDP6 checksums on RX? */ | 52 | no_check6_rx:1,/* Allow zero UDP6 checksums on RX? */ |
53 | convert_csum:1;/* On receive, convert checksum | ||
54 | * unnecessary to checksum complete | ||
55 | * if possible. | ||
56 | */ | ||
53 | /* | 57 | /* |
54 | * Following member retains the information to create a UDP header | 58 | * Following member retains the information to create a UDP header |
55 | * when the socket is uncorked. | 59 | * when the socket is uncorked. |
@@ -98,6 +102,16 @@ static inline bool udp_get_no_check6_rx(struct sock *sk) | |||
98 | return udp_sk(sk)->no_check6_rx; | 102 | return udp_sk(sk)->no_check6_rx; |
99 | } | 103 | } |
100 | 104 | ||
105 | static inline void udp_set_convert_csum(struct sock *sk, bool val) | ||
106 | { | ||
107 | udp_sk(sk)->convert_csum = val; | ||
108 | } | ||
109 | |||
110 | static inline bool udp_get_convert_csum(struct sock *sk) | ||
111 | { | ||
112 | return udp_sk(sk)->convert_csum; | ||
113 | } | ||
114 | |||
101 | #define udp_portaddr_for_each_entry(__sk, node, list) \ | 115 | #define udp_portaddr_for_each_entry(__sk, node, list) \ |
102 | hlist_nulls_for_each_entry(__sk, node, list, __sk_common.skc_portaddr_node) | 116 | hlist_nulls_for_each_entry(__sk, node, list, __sk_common.skc_portaddr_node) |
103 | 117 | ||
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 3549c21fe5f7..0da3849fd35b 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -1788,6 +1788,10 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, | |||
1788 | if (sk != NULL) { | 1788 | if (sk != NULL) { |
1789 | int ret; | 1789 | int ret; |
1790 | 1790 | ||
1791 | if (udp_sk(sk)->convert_csum && uh->check && !IS_UDPLITE(sk)) | ||
1792 | skb_checksum_try_convert(skb, IPPROTO_UDP, uh->check, | ||
1793 | inet_compute_pseudo); | ||
1794 | |||
1791 | ret = udp_queue_rcv_skb(sk, skb); | 1795 | ret = udp_queue_rcv_skb(sk, skb); |
1792 | sock_put(sk); | 1796 | sock_put(sk); |
1793 | 1797 | ||
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index a6adff98382a..84e0e05c9c0e 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c | |||
@@ -290,16 +290,25 @@ static struct sk_buff **udp4_gro_receive(struct sk_buff **head, | |||
290 | { | 290 | { |
291 | struct udphdr *uh = udp_gro_udphdr(skb); | 291 | struct udphdr *uh = udp_gro_udphdr(skb); |
292 | 292 | ||
293 | /* Don't bother verifying checksum if we're going to flush anyway. */ | 293 | if (unlikely(!uh)) |
294 | if (unlikely(!uh) || | 294 | goto flush; |
295 | (!NAPI_GRO_CB(skb)->flush && | ||
296 | skb_gro_checksum_validate_zero_check(skb, IPPROTO_UDP, uh->check, | ||
297 | inet_gro_compute_pseudo))) { | ||
298 | NAPI_GRO_CB(skb)->flush = 1; | ||
299 | return NULL; | ||
300 | } | ||
301 | 295 | ||
296 | /* Don't bother verifying checksum if we're going to flush anyway. */ | ||
297 | if (!NAPI_GRO_CB(skb)->flush) | ||
298 | goto skip; | ||
299 | |||
300 | if (skb_gro_checksum_validate_zero_check(skb, IPPROTO_UDP, uh->check, | ||
301 | inet_gro_compute_pseudo)) | ||
302 | goto flush; | ||
303 | else if (uh->check) | ||
304 | skb_gro_checksum_try_convert(skb, IPPROTO_UDP, uh->check, | ||
305 | inet_gro_compute_pseudo); | ||
306 | skip: | ||
302 | return udp_gro_receive(head, skb, uh); | 307 | return udp_gro_receive(head, skb, uh); |
308 | |||
309 | flush: | ||
310 | NAPI_GRO_CB(skb)->flush = 1; | ||
311 | return NULL; | ||
303 | } | 312 | } |
304 | 313 | ||
305 | int udp_gro_complete(struct sk_buff *skb, int nhoff) | 314 | int udp_gro_complete(struct sk_buff *skb, int nhoff) |
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 12fcce8fba46..f6ba535b6feb 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -891,6 +891,10 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, | |||
891 | goto csum_error; | 891 | goto csum_error; |
892 | } | 892 | } |
893 | 893 | ||
894 | if (udp_sk(sk)->convert_csum && uh->check && !IS_UDPLITE(sk)) | ||
895 | skb_checksum_try_convert(skb, IPPROTO_UDP, uh->check, | ||
896 | ip6_compute_pseudo); | ||
897 | |||
894 | ret = udpv6_queue_rcv_skb(sk, skb); | 898 | ret = udpv6_queue_rcv_skb(sk, skb); |
895 | sock_put(sk); | 899 | sock_put(sk); |
896 | 900 | ||
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index b13e377e9c53..89cb9a9b8537 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c | |||
@@ -134,16 +134,26 @@ static struct sk_buff **udp6_gro_receive(struct sk_buff **head, | |||
134 | { | 134 | { |
135 | struct udphdr *uh = udp_gro_udphdr(skb); | 135 | struct udphdr *uh = udp_gro_udphdr(skb); |
136 | 136 | ||
137 | if (unlikely(!uh)) | ||
138 | goto flush; | ||
139 | |||
137 | /* Don't bother verifying checksum if we're going to flush anyway. */ | 140 | /* Don't bother verifying checksum if we're going to flush anyway. */ |
138 | if (unlikely(!uh) || | 141 | if (!NAPI_GRO_CB(skb)->flush) |
139 | (!NAPI_GRO_CB(skb)->flush && | 142 | goto skip; |
140 | skb_gro_checksum_validate_zero_check(skb, IPPROTO_UDP, uh->check, | ||
141 | ip6_gro_compute_pseudo))) { | ||
142 | NAPI_GRO_CB(skb)->flush = 1; | ||
143 | return NULL; | ||
144 | } | ||
145 | 143 | ||
144 | if (skb_gro_checksum_validate_zero_check(skb, IPPROTO_UDP, uh->check, | ||
145 | ip6_gro_compute_pseudo)) | ||
146 | goto flush; | ||
147 | else if (uh->check) | ||
148 | skb_gro_checksum_try_convert(skb, IPPROTO_UDP, uh->check, | ||
149 | ip6_gro_compute_pseudo); | ||
150 | |||
151 | skip: | ||
146 | return udp_gro_receive(head, skb, uh); | 152 | return udp_gro_receive(head, skb, uh); |
153 | |||
154 | flush: | ||
155 | NAPI_GRO_CB(skb)->flush = 1; | ||
156 | return NULL; | ||
147 | } | 157 | } |
148 | 158 | ||
149 | int udp6_gro_complete(struct sk_buff *skb, int nhoff) | 159 | int udp6_gro_complete(struct sk_buff *skb, int nhoff) |