From patchwork Sat Nov 1 22:58:03 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Herbert X-Patchwork-Id: 405816 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id D6F35140081 for ; Sun, 2 Nov 2014 09:58:48 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752963AbaKAW6p (ORCPT ); Sat, 1 Nov 2014 18:58:45 -0400 Received: from mail-ig0-f180.google.com ([209.85.213.180]:48986 "EHLO mail-ig0-f180.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752479AbaKAW6h (ORCPT ); Sat, 1 Nov 2014 18:58:37 -0400 Received: by mail-ig0-f180.google.com with SMTP id h3so2827192igd.13 for ; Sat, 01 Nov 2014 15:58:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:subject:date:message-id:in-reply-to:references; bh=hyruVA1RjpAcDzQ2LeoUYkdOjsYReVCo8PDj/tjFA8o=; b=D1f3UQrsjwqtwNT9e8Yzxv0zSMGFs5Mb7UuP8q5xH/AayrACJzl9jPrxxsRMAixEC+ KuobdszfqSnn1KVoHT+jonRcKceRgX2UMP6pgy+0FlidxpIYwzFSUNhQj5kBENOGwQxK 1uhGJpopzdg5/gRh7W47QAv8wMF1ZD76PePhJsc9/p5IKsol0Saei3qSj1CxhkpKpp3V J6HTE2BAXR8IqWio4uS1erTjx9ufA42Oylh23FFR7iiRF+/nXIPklRnMGZF/q3n/AZA5 PJ5vODQlvGQ3VektPID00V9CPDNP2yzF0d3epBqe1iCTKZlYoVe9jWFYQuYdqEW6cQk1 6Qow== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=hyruVA1RjpAcDzQ2LeoUYkdOjsYReVCo8PDj/tjFA8o=; b=mSVpNVGrTb7/7Pj+Fy068xXEIJQE7tTV2jR8BlScnxMH4TA5YypHKndyjcQ5DLqau6 ZJ2joG5qPsOasUJKEyT8Tqboee25u1yTrLVwHi69cF/9ehNGKYoBRUpSK3vb43yrtbJs n5uXIyQFgrFYjkDHZUOad1hDLJw2d83PaL4WS5HKcMVrGbuwqG9XwWmwwZ1x4vd/805f xRShS3dFzHeSFXj+wYjnPClmp5Wlz9NG119wg0/2Zcfl5D7WASpvBCVyqAMqN4jqbk38 PGXS1fHFhPICAb8BBmBtmRoQeGzOgzaU8nXNRITL95BT+tt70OKs3IJnT4pNOXB3KELR 8gKQ== X-Gm-Message-State: ALoCoQk2RG3jVvUBiCyQ8Ie8uDbx/TkwH8/cco/20W9PGo4uXB3xT5+RI4OMNIj/YayAZ8SYQV9F X-Received: by 10.42.250.200 with SMTP id mp8mr31991707icb.29.1414882716397; Sat, 01 Nov 2014 15:58:36 -0700 (PDT) Received: from tomh.mtv.corp.google.com ([172.18.117.126]) by mx.google.com with ESMTPSA id x193sm7271646iod.17.2014.11.01.15.58.35 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Sat, 01 Nov 2014 15:58:36 -0700 (PDT) From: Tom Herbert To: davem@davemloft.net, netdev@vger.kernel.org Subject: [PATCH net-next 7/7] gue: Receive side of remote checksum offload Date: Sat, 1 Nov 2014 15:58:03 -0700 Message-Id: <1414882683-25484-8-git-send-email-therbert@google.com> X-Mailer: git-send-email 2.1.0.rc2.206.gedb03e5 In-Reply-To: <1414882683-25484-1-git-send-email-therbert@google.com> References: <1414882683-25484-1-git-send-email-therbert@google.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add processing of the remote checksum offload option in both the normal path as well as the GRO path. The implements patching the affected checksum to derive the offloaded checksum. Signed-off-by: Tom Herbert --- net/ipv4/fou.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 161 insertions(+), 9 deletions(-) diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c index fb0db99..740ae09 100644 --- a/net/ipv4/fou.c +++ b/net/ipv4/fou.c @@ -63,6 +63,59 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb) return -fou->protocol; } +static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr, + void *data, int hdrlen, u8 ipproto) +{ + __be16 *pd = data; + u16 start = ntohs(pd[0]); + u16 offset = ntohs(pd[1]); + u16 poffset = 0; + u16 plen; + __wsum csum, delta; + __sum16 *psum; + + if (skb->remcsum_offload) { + /* Already processed in GRO path */ + skb->remcsum_offload = 0; + 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)) + return NULL; + guehdr = (struct guehdr *)&udp_hdr(skb)[1]; + + if (ipproto == IPPROTO_IP && sizeof(struct iphdr) < plen) { + struct iphdr *ip = (struct iphdr *)(skb->data + 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(skb->csum, skb_checksum(skb, poffset + hdrlen, + start - poffset - hdrlen, 0)); + + /* Set derived checksum in packet */ + psum = (__sum16 *)(skb->data + hdrlen + offset); + delta = csum_sub(csum_fold(csum), *psum); + *psum = csum_fold(csum); + + /* Adjust skb->csum since we changed the packet */ + skb->csum = csum_add(skb->csum, delta); + + return guehdr; +} + static int gue_control_message(struct sk_buff *skb, struct guehdr *guehdr) { /* No support yet */ @@ -76,6 +129,7 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) size_t len, optlen, hdrlen; struct guehdr *guehdr; void *data; + u16 doffset = 0; if (!fou) return 1; @@ -100,20 +154,43 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) if (guehdr->version != 0 || validate_gue_flags(guehdr, optlen)) goto drop; - /* Pull UDP and GUE headers */ - fou_recv_pull(skb, len); + hdrlen = sizeof(struct guehdr) + optlen; + + 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 + * there is a remote checksum offload. + */ + skb_postpull_rcsum(skb, udp_hdr(skb), len); data = &guehdr[1]; if (guehdr->flags & GUE_FLAG_PRIV) { - data += GUE_LEN_PRIV; + __be32 flags = *(__be32 *)(data + doffset); + + doffset += GUE_LEN_PRIV; - /* Process private flags */ + if (flags & GUE_PFLAG_REMCSUM) { + guehdr = gue_remcsum(skb, guehdr, data + doffset, + hdrlen, guehdr->proto_ctype); + if (!guehdr) + goto drop; + + data = &guehdr[1]; + + doffset += GUE_PLEN_REMCSUM; + } } if (unlikely(guehdr->control)) return gue_control_message(skb, guehdr); + __skb_pull(skb, hdrlen); + skb_reset_transport_header(skb); + return -guehdr->proto_ctype; drop: @@ -164,6 +241,66 @@ out_unlock: return err; } +static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off, + struct guehdr *guehdr, void *data, + size_t hdrlen, u8 ipproto) +{ + __be16 *pd = data; + u16 start = ntohs(pd[0]); + u16 offset = ntohs(pd[1]); + u16 poffset = 0; + u16 plen; + void *ptr; + __wsum csum, delta; + __sum16 *psum; + + if (skb->remcsum_offload) + return guehdr; + + if (start > skb_gro_len(skb) - hdrlen || + offset > skb_gro_len(skb) - hdrlen - sizeof(u16) || + !NAPI_GRO_CB(skb)->csum_valid || skb->remcsum_offload) + return NULL; + + plen = hdrlen + offset + sizeof(u16); + + /* Pull checksum that will be written */ + if (skb_gro_header_hard(skb, off + plen)) { + guehdr = skb_gro_header_slow(skb, off + plen, off); + if (!guehdr) + return NULL; + } + + ptr = (void *)guehdr + hdrlen; + + 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 */ + skb->csum = csum_add(skb->csum, delta); + NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta); + + skb->remcsum_offload = 1; + + return guehdr; +} + static struct sk_buff **gue_gro_receive(struct sk_buff **head, struct sk_buff *skb) { @@ -174,6 +311,7 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head, struct guehdr *guehdr; size_t len, optlen, hdrlen, off; void *data; + u16 doffset = 0; int flush = 1; off = skb_gro_offset(skb); @@ -201,19 +339,33 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head, hdrlen = sizeof(*guehdr) + optlen; - skb_gro_pull(skb, hdrlen); - - /* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/ + /* Adjust NAPI_GRO_CB(skb)->csum to account for guehdr, + * this is needed if there is a remote checkcsum offload. + */ skb_gro_postpull_rcsum(skb, guehdr, hdrlen); data = &guehdr[1]; if (guehdr->flags & GUE_FLAG_PRIV) { - data += GUE_LEN_PRIV; + __be32 flags = *(__be32 *)(data + doffset); - /* Process private flags */ + doffset += GUE_LEN_PRIV; + + if (flags & GUE_PFLAG_REMCSUM) { + guehdr = gue_gro_remcsum(skb, off, guehdr, + data + doffset, hdrlen, + guehdr->proto_ctype); + if (!guehdr) + goto out; + + data = &guehdr[1]; + + doffset += GUE_PLEN_REMCSUM; + } } + skb_gro_pull(skb, hdrlen); + flush = 0; for (p = *head; p; p = p->next) {