aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2014-11-26 12:25:51 -0500
committerDavid S. Miller <davem@davemloft.net>2014-11-26 12:25:51 -0500
commit12bbd595ab45f093db36d12768926422013b3441 (patch)
tree553e4f9745b404e0d1db149192e303fd51681e80
parentced7a04e394fe1df41d13b833baab5cfcb1d03b3 (diff)
parent4fd671ded14f92cb8db0bf72747f4df508ba5e3d (diff)
Merge branch 'remcsum_adjust'
Tom Herbert says: ==================== gue: Generalize remote checksum offload The remote checksum offload is generalized by creating a common function (remcsum_adjust) that does the work of modifying the checksum in remote checksum offload. This function can be called from normal or GRO path. GUE was modified to use this function. Remote checksum offload is described in https://tools.ietf.org/html/draft-herbert-remotecsumoffload-01 Tested by running 200 TCP_STREAM connections over GUE, did not see any problems with remote checksum offload enabled. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/checksum.h16
-rw-r--r--net/ipv4/fou.c84
2 files changed, 33 insertions, 67 deletions
diff --git a/include/net/checksum.h b/include/net/checksum.h
index 6465bae80a4f..e339a9513e29 100644
--- a/include/net/checksum.h
+++ b/include/net/checksum.h
@@ -151,4 +151,20 @@ static inline void inet_proto_csum_replace2(__sum16 *sum, struct sk_buff *skb,
151 (__force __be32)to, pseudohdr); 151 (__force __be32)to, pseudohdr);
152} 152}
153 153
154static inline __wsum remcsum_adjust(void *ptr, __wsum csum,
155 int start, int offset)
156{
157 __sum16 *psum = (__sum16 *)(ptr + offset);
158 __wsum delta;
159
160 /* Subtract out checksum up to start */
161 csum = csum_sub(csum, csum_partial(ptr, start, 0));
162
163 /* Set derived checksum in packet */
164 delta = csum_sub(csum_fold(csum), *psum);
165 *psum = csum_fold(csum);
166
167 return delta;
168}
169
154#endif 170#endif
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
index 3dfe9828e7ef..b986298a7ba3 100644
--- a/net/ipv4/fou.c
+++ b/net/ipv4/fou.c
@@ -64,15 +64,13 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
64} 64}
65 65
66static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr, 66static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr,
67 void *data, int hdrlen, u8 ipproto) 67 void *data, size_t hdrlen, u8 ipproto)
68{ 68{
69 __be16 *pd = data; 69 __be16 *pd = data;
70 u16 start = ntohs(pd[0]); 70 size_t start = ntohs(pd[0]);
71 u16 offset = ntohs(pd[1]); 71 size_t offset = ntohs(pd[1]);
72 u16 poffset = 0; 72 size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start);
73 u16 plen; 73 __wsum delta;
74 __wsum csum, delta;
75 __sum16 *psum;
76 74
77 if (skb->remcsum_offload) { 75 if (skb->remcsum_offload) {
78 /* Already processed in GRO path */ 76 /* Already processed in GRO path */
@@ -80,35 +78,15 @@ static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr,
80 return guehdr; 78 return guehdr;
81 } 79 }
82 80
83 if (start > skb->len - hdrlen ||
84 offset > skb->len - hdrlen - sizeof(u16))
85 return NULL;
86
87 if (unlikely(skb->ip_summed != CHECKSUM_COMPLETE))
88 __skb_checksum_complete(skb);
89
90 plen = hdrlen + offset + sizeof(u16);
91 if (!pskb_may_pull(skb, plen)) 81 if (!pskb_may_pull(skb, plen))
92 return NULL; 82 return NULL;
93 guehdr = (struct guehdr *)&udp_hdr(skb)[1]; 83 guehdr = (struct guehdr *)&udp_hdr(skb)[1];
94 84
95 if (ipproto == IPPROTO_IP && sizeof(struct iphdr) < plen) { 85 if (unlikely(skb->ip_summed != CHECKSUM_COMPLETE))
96 struct iphdr *ip = (struct iphdr *)(skb->data + hdrlen); 86 __skb_checksum_complete(skb);
97
98 /* If next header happens to be IP we can skip that for the
99 * checksum calculation since the IP header checksum is zero
100 * if correct.
101 */
102 poffset = ip->ihl * 4;
103 }
104
105 csum = csum_sub(skb->csum, skb_checksum(skb, poffset + hdrlen,
106 start - poffset - hdrlen, 0));
107 87
108 /* Set derived checksum in packet */ 88 delta = remcsum_adjust((void *)guehdr + hdrlen,
109 psum = (__sum16 *)(skb->data + hdrlen + offset); 89 skb->csum, start, offset);
110 delta = csum_sub(csum_fold(csum), *psum);
111 *psum = csum_fold(csum);
112 90
113 /* Adjust skb->csum since we changed the packet */ 91 /* Adjust skb->csum since we changed the packet */
114 skb->csum = csum_add(skb->csum, delta); 92 skb->csum = csum_add(skb->csum, delta);
@@ -158,9 +136,6 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
158 136
159 ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len); 137 ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len);
160 138
161 /* Pull UDP header now, skb->data points to guehdr */
162 __skb_pull(skb, sizeof(struct udphdr));
163
164 /* Pull csum through the guehdr now . This can be used if 139 /* Pull csum through the guehdr now . This can be used if
165 * there is a remote checksum offload. 140 * there is a remote checksum offload.
166 */ 141 */
@@ -188,7 +163,7 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
188 if (unlikely(guehdr->control)) 163 if (unlikely(guehdr->control))
189 return gue_control_message(skb, guehdr); 164 return gue_control_message(skb, guehdr);
190 165
191 __skb_pull(skb, hdrlen); 166 __skb_pull(skb, sizeof(struct udphdr) + hdrlen);
192 skb_reset_transport_header(skb); 167 skb_reset_transport_header(skb);
193 168
194 return -guehdr->proto_ctype; 169 return -guehdr->proto_ctype;
@@ -248,24 +223,17 @@ static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
248 size_t hdrlen, u8 ipproto) 223 size_t hdrlen, u8 ipproto)
249{ 224{
250 __be16 *pd = data; 225 __be16 *pd = data;
251 u16 start = ntohs(pd[0]); 226 size_t start = ntohs(pd[0]);
252 u16 offset = ntohs(pd[1]); 227 size_t offset = ntohs(pd[1]);
253 u16 poffset = 0; 228 size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start);
254 u16 plen; 229 __wsum delta;
255 void *ptr;
256 __wsum csum, delta;
257 __sum16 *psum;
258 230
259 if (skb->remcsum_offload) 231 if (skb->remcsum_offload)
260 return guehdr; 232 return guehdr;
261 233
262 if (start > skb_gro_len(skb) - hdrlen || 234 if (!NAPI_GRO_CB(skb)->csum_valid)
263 offset > skb_gro_len(skb) - hdrlen - sizeof(u16) ||
264 !NAPI_GRO_CB(skb)->csum_valid || skb->remcsum_offload)
265 return NULL; 235 return NULL;
266 236
267 plen = hdrlen + offset + sizeof(u16);
268
269 /* Pull checksum that will be written */ 237 /* Pull checksum that will be written */
270 if (skb_gro_header_hard(skb, off + plen)) { 238 if (skb_gro_header_hard(skb, off + plen)) {
271 guehdr = skb_gro_header_slow(skb, off + plen, off); 239 guehdr = skb_gro_header_slow(skb, off + plen, off);
@@ -273,26 +241,8 @@ static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
273 return NULL; 241 return NULL;
274 } 242 }
275 243
276 ptr = (void *)guehdr + hdrlen; 244 delta = remcsum_adjust((void *)guehdr + hdrlen,
277 245 NAPI_GRO_CB(skb)->csum, start, offset);
278 if (ipproto == IPPROTO_IP &&
279 (hdrlen + sizeof(struct iphdr) < plen)) {
280 struct iphdr *ip = (struct iphdr *)(ptr + hdrlen);
281
282 /* If next header happens to be IP we can skip
283 * that for the checksum calculation since the
284 * IP header checksum is zero if correct.
285 */
286 poffset = ip->ihl * 4;
287 }
288
289 csum = csum_sub(NAPI_GRO_CB(skb)->csum,
290 csum_partial(ptr + poffset, start - poffset, 0));
291
292 /* Set derived checksum in packet */
293 psum = (__sum16 *)(ptr + offset);
294 delta = csum_sub(csum_fold(csum), *psum);
295 *psum = csum_fold(csum);
296 246
297 /* Adjust skb->csum since we changed the packet */ 247 /* Adjust skb->csum since we changed the packet */
298 skb->csum = csum_add(skb->csum, delta); 248 skb->csum = csum_add(skb->csum, delta);