Commit 12bbd595 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents ced7a04e 4fd671de
...@@ -151,4 +151,20 @@ static inline void inet_proto_csum_replace2(__sum16 *sum, struct sk_buff *skb, ...@@ -151,4 +151,20 @@ static inline void inet_proto_csum_replace2(__sum16 *sum, struct sk_buff *skb,
(__force __be32)to, pseudohdr); (__force __be32)to, pseudohdr);
} }
static inline __wsum remcsum_adjust(void *ptr, __wsum csum,
int start, int offset)
{
__sum16 *psum = (__sum16 *)(ptr + offset);
__wsum delta;
/* Subtract out checksum up to start */
csum = csum_sub(csum, csum_partial(ptr, start, 0));
/* Set derived checksum in packet */
delta = csum_sub(csum_fold(csum), *psum);
*psum = csum_fold(csum);
return delta;
}
#endif #endif
...@@ -64,15 +64,13 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb) ...@@ -64,15 +64,13 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
} }
static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr, static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr,
void *data, int hdrlen, u8 ipproto) void *data, size_t hdrlen, u8 ipproto)
{ {
__be16 *pd = data; __be16 *pd = data;
u16 start = ntohs(pd[0]); size_t start = ntohs(pd[0]);
u16 offset = ntohs(pd[1]); size_t offset = ntohs(pd[1]);
u16 poffset = 0; size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start);
u16 plen; __wsum delta;
__wsum csum, delta;
__sum16 *psum;
if (skb->remcsum_offload) { if (skb->remcsum_offload) {
/* Already processed in GRO path */ /* Already processed in GRO path */
...@@ -80,35 +78,15 @@ static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr, ...@@ -80,35 +78,15 @@ static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr,
return guehdr; return guehdr;
} }
if (start > skb->len - hdrlen ||
offset > skb->len - hdrlen - sizeof(u16))
return NULL;
if (unlikely(skb->ip_summed != CHECKSUM_COMPLETE))
__skb_checksum_complete(skb);
plen = hdrlen + offset + sizeof(u16);
if (!pskb_may_pull(skb, plen)) if (!pskb_may_pull(skb, plen))
return NULL; return NULL;
guehdr = (struct guehdr *)&udp_hdr(skb)[1]; guehdr = (struct guehdr *)&udp_hdr(skb)[1];
if (ipproto == IPPROTO_IP && sizeof(struct iphdr) < plen) { if (unlikely(skb->ip_summed != CHECKSUM_COMPLETE))
struct iphdr *ip = (struct iphdr *)(skb->data + hdrlen); __skb_checksum_complete(skb);
/* If next header happens to be IP we can skip that for the
* checksum calculation since the IP header checksum is zero
* if correct.
*/
poffset = ip->ihl * 4;
}
csum = csum_sub(skb->csum, skb_checksum(skb, poffset + hdrlen,
start - poffset - hdrlen, 0));
/* Set derived checksum in packet */ delta = remcsum_adjust((void *)guehdr + hdrlen,
psum = (__sum16 *)(skb->data + hdrlen + offset); skb->csum, start, offset);
delta = csum_sub(csum_fold(csum), *psum);
*psum = csum_fold(csum);
/* Adjust skb->csum since we changed the packet */ /* Adjust skb->csum since we changed the packet */
skb->csum = csum_add(skb->csum, delta); skb->csum = csum_add(skb->csum, delta);
...@@ -158,9 +136,6 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) ...@@ -158,9 +136,6 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len); ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len);
/* Pull UDP header now, skb->data points to guehdr */
__skb_pull(skb, sizeof(struct udphdr));
/* Pull csum through the guehdr now . This can be used if /* Pull csum through the guehdr now . This can be used if
* there is a remote checksum offload. * there is a remote checksum offload.
*/ */
...@@ -188,7 +163,7 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) ...@@ -188,7 +163,7 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
if (unlikely(guehdr->control)) if (unlikely(guehdr->control))
return gue_control_message(skb, guehdr); return gue_control_message(skb, guehdr);
__skb_pull(skb, hdrlen); __skb_pull(skb, sizeof(struct udphdr) + hdrlen);
skb_reset_transport_header(skb); skb_reset_transport_header(skb);
return -guehdr->proto_ctype; return -guehdr->proto_ctype;
...@@ -248,24 +223,17 @@ static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off, ...@@ -248,24 +223,17 @@ static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
size_t hdrlen, u8 ipproto) size_t hdrlen, u8 ipproto)
{ {
__be16 *pd = data; __be16 *pd = data;
u16 start = ntohs(pd[0]); size_t start = ntohs(pd[0]);
u16 offset = ntohs(pd[1]); size_t offset = ntohs(pd[1]);
u16 poffset = 0; size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start);
u16 plen; __wsum delta;
void *ptr;
__wsum csum, delta;
__sum16 *psum;
if (skb->remcsum_offload) if (skb->remcsum_offload)
return guehdr; return guehdr;
if (start > skb_gro_len(skb) - hdrlen || if (!NAPI_GRO_CB(skb)->csum_valid)
offset > skb_gro_len(skb) - hdrlen - sizeof(u16) ||
!NAPI_GRO_CB(skb)->csum_valid || skb->remcsum_offload)
return NULL; return NULL;
plen = hdrlen + offset + sizeof(u16);
/* Pull checksum that will be written */ /* Pull checksum that will be written */
if (skb_gro_header_hard(skb, off + plen)) { if (skb_gro_header_hard(skb, off + plen)) {
guehdr = skb_gro_header_slow(skb, off + plen, off); 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,26 +241,8 @@ static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
return NULL; return NULL;
} }
ptr = (void *)guehdr + hdrlen; delta = remcsum_adjust((void *)guehdr + hdrlen,
NAPI_GRO_CB(skb)->csum, start, offset);
if (ipproto == IPPROTO_IP &&
(hdrlen + sizeof(struct iphdr) < plen)) {
struct iphdr *ip = (struct iphdr *)(ptr + hdrlen);
/* If next header happens to be IP we can skip
* that for the checksum calculation since the
* IP header checksum is zero if correct.
*/
poffset = ip->ihl * 4;
}
csum = csum_sub(NAPI_GRO_CB(skb)->csum,
csum_partial(ptr + poffset, start - poffset, 0));
/* Set derived checksum in packet */
psum = (__sum16 *)(ptr + offset);
delta = csum_sub(csum_fold(csum), *psum);
*psum = csum_fold(csum);
/* Adjust skb->csum since we changed the packet */ /* Adjust skb->csum since we changed the packet */
skb->csum = csum_add(skb->csum, delta); skb->csum = csum_add(skb->csum, delta);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment