All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next 0/8] net: qualcomm: rmnet: MAPv4 download checksum cleanup, part 1
@ 2021-06-11 19:05 Alex Elder
  2021-06-11 19:05 ` [PATCH net-next 1/8] net: qualcomm: rmnet: use ip_is_fragment() Alex Elder
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Alex Elder @ 2021-06-11 19:05 UTC (permalink / raw)
  To: subashab, stranche, davem, kuba; +Cc: bjorn.andersson, netdev, linux-kernel

I'm posting a large series an two smaller parts; this is part 1.

The RMNet driver handles MAP (or QMAP) protocol traffic.  There are
several versions of this protocol.  Version 1 supports multiplexing,
as well as aggregation of packets in a single buffer.  Version 4
adds the ability to perform checksum offload.  And version 5
implements checksum offload in a different way from version 4.

This series involves only MAPv4 protocol checksum offload, and only
in the download (RX) direction.  It affects handling of checksums
computed by hardware for UDP datagrams and TCP segments, carried
over both IPv4 and IPv6.

MAP packets arriving on an RMNet port implementing MAPv4 checksum
offload are passed to rmnet_map_checksum_downlink_packet() for
handling.

The packet is then passed to rmnet_map_ipv4_dl_csum_trailer() or
rmnet_map_ipv6_dl_csum_trailer(), depending contents of the MAP
payload.  These two functions interpret checksum metadata to
determine whether the checksum in the received packet matches that
calculated by the hardware.

It is these two functions that are the subject of this series (parts
1 and 2).  The bulk of these functions are transformed--in a lot of
small steps--from an extremely difficult-to-follow block of checksum
processing code into a fairly simple, heavily commented equivalent.

					-Alex

Alex Elder (8):
  net: qualcomm: rmnet: use ip_is_fragment()
  net: qualcomm: rmnet: eliminate some ifdefs
  net: qualcomm: rmnet: get rid of some local variables
  net: qualcomm: rmnet: simplify rmnet_map_get_csum_field()
  net: qualcomm: rmnet: IPv4 header has zero checksum
  net: qualcomm: rmnet: clarify a bit of code
  net: qualcomm: rmnet: avoid unnecessary byte-swapping
  net: qualcomm: rmnet: avoid unnecessary IPv6 byte-swapping

 .../ethernet/qualcomm/rmnet/rmnet_config.h    |   1 +
 .../ethernet/qualcomm/rmnet/rmnet_map_data.c  | 179 +++++++++---------
 .../net/ethernet/qualcomm/rmnet/rmnet_vnd.c   |   1 +
 3 files changed, 92 insertions(+), 89 deletions(-)

-- 
2.27.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH net-next 1/8] net: qualcomm: rmnet: use ip_is_fragment()
  2021-06-11 19:05 [PATCH net-next 0/8] net: qualcomm: rmnet: MAPv4 download checksum cleanup, part 1 Alex Elder
@ 2021-06-11 19:05 ` Alex Elder
  2021-06-11 19:05 ` [PATCH net-next 2/8] net: qualcomm: rmnet: eliminate some ifdefs Alex Elder
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Alex Elder @ 2021-06-11 19:05 UTC (permalink / raw)
  To: subashab, stranche, davem, kuba; +Cc: bjorn.andersson, netdev, linux-kernel

In rmnet_map_ipv4_dl_csum_trailer() use ip_is_fragment() to
determine whether a socket buffer contains a packet fragment.

Signed-off-by: Alex Elder <elder@linaro.org>
---
 drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
index cecf72be51029..34bd1a98a1015 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
@@ -50,8 +50,9 @@ rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb,
 	__be16 addend;
 
 	ip4h = (struct iphdr *)(skb->data);
-	if ((ntohs(ip4h->frag_off) & IP_MF) ||
-	    ((ntohs(ip4h->frag_off) & IP_OFFSET) > 0)) {
+
+	/* We don't support checksum offload on IPv4 fragments */
+	if (ip_is_fragment(ip4h)) {
 		priv->stats.csum_fragmented_pkt++;
 		return -EOPNOTSUPP;
 	}
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH net-next 2/8] net: qualcomm: rmnet: eliminate some ifdefs
  2021-06-11 19:05 [PATCH net-next 0/8] net: qualcomm: rmnet: MAPv4 download checksum cleanup, part 1 Alex Elder
  2021-06-11 19:05 ` [PATCH net-next 1/8] net: qualcomm: rmnet: use ip_is_fragment() Alex Elder
@ 2021-06-11 19:05 ` Alex Elder
  2021-06-11 19:05 ` [PATCH net-next 3/8] net: qualcomm: rmnet: get rid of some local variables Alex Elder
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Alex Elder @ 2021-06-11 19:05 UTC (permalink / raw)
  To: subashab, stranche, davem, kuba; +Cc: bjorn.andersson, netdev, linux-kernel

If IPV6 is not enabled in the kernel configuration, the RMNet
checksum code indicates a buffer containing an IPv6 packet is not
supported.  The same thing happens if a buffer contains something
other than an IPv4 or IPv6 packet.

We can rearrange things a bit in two functions so that some #ifdef
calls can simply be eliminated.

Signed-off-by: Alex Elder <elder@linaro.org>
---
 .../ethernet/qualcomm/rmnet/rmnet_map_data.c  | 54 ++++++++-----------
 1 file changed, 23 insertions(+), 31 deletions(-)

diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
index 34bd1a98a1015..b8e504ac7fb1e 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
@@ -431,21 +431,15 @@ int rmnet_map_checksum_downlink_packet(struct sk_buff *skb, u16 len)
 		return -EINVAL;
 	}
 
-	if (skb->protocol == htons(ETH_P_IP)) {
+	if (skb->protocol == htons(ETH_P_IP))
 		return rmnet_map_ipv4_dl_csum_trailer(skb, csum_trailer, priv);
-	} else if (skb->protocol == htons(ETH_P_IPV6)) {
-#if IS_ENABLED(CONFIG_IPV6)
+
+	if (IS_ENABLED(CONFIG_IPV6) && skb->protocol == htons(ETH_P_IPV6))
 		return rmnet_map_ipv6_dl_csum_trailer(skb, csum_trailer, priv);
-#else
-		priv->stats.csum_err_invalid_ip_version++;
-		return -EPROTONOSUPPORT;
-#endif
-	} else {
-		priv->stats.csum_err_invalid_ip_version++;
-		return -EPROTONOSUPPORT;
-	}
 
-	return 0;
+	priv->stats.csum_err_invalid_ip_version++;
+
+	return -EPROTONOSUPPORT;
 }
 
 static void rmnet_map_v4_checksum_uplink_packet(struct sk_buff *skb,
@@ -462,28 +456,26 @@ static void rmnet_map_v4_checksum_uplink_packet(struct sk_buff *skb,
 		     (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))))
 		goto sw_csum;
 
-	if (skb->ip_summed == CHECKSUM_PARTIAL) {
-		iphdr = (char *)ul_header +
-			sizeof(struct rmnet_map_ul_csum_header);
+	if (skb->ip_summed != CHECKSUM_PARTIAL)
+		goto sw_csum;
 
-		if (skb->protocol == htons(ETH_P_IP)) {
-			rmnet_map_ipv4_ul_csum_header(iphdr, ul_header, skb);
-			priv->stats.csum_hw++;
-			return;
-		} else if (skb->protocol == htons(ETH_P_IPV6)) {
-#if IS_ENABLED(CONFIG_IPV6)
-			rmnet_map_ipv6_ul_csum_header(iphdr, ul_header, skb);
-			priv->stats.csum_hw++;
-			return;
-#else
-			priv->stats.csum_err_invalid_ip_version++;
-			goto sw_csum;
-#endif
-		} else {
-			priv->stats.csum_err_invalid_ip_version++;
-		}
+	iphdr = (char *)ul_header +
+		sizeof(struct rmnet_map_ul_csum_header);
+
+	if (skb->protocol == htons(ETH_P_IP)) {
+		rmnet_map_ipv4_ul_csum_header(iphdr, ul_header, skb);
+		priv->stats.csum_hw++;
+		return;
+	}
+
+	if (IS_ENABLED(CONFIG_IPV6) && skb->protocol == htons(ETH_P_IPV6)) {
+		rmnet_map_ipv6_ul_csum_header(iphdr, ul_header, skb);
+		priv->stats.csum_hw++;
+		return;
 	}
 
+	priv->stats.csum_err_invalid_ip_version++;
+
 sw_csum:
 	memset(ul_header, 0, sizeof(*ul_header));
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH net-next 3/8] net: qualcomm: rmnet: get rid of some local variables
  2021-06-11 19:05 [PATCH net-next 0/8] net: qualcomm: rmnet: MAPv4 download checksum cleanup, part 1 Alex Elder
  2021-06-11 19:05 ` [PATCH net-next 1/8] net: qualcomm: rmnet: use ip_is_fragment() Alex Elder
  2021-06-11 19:05 ` [PATCH net-next 2/8] net: qualcomm: rmnet: eliminate some ifdefs Alex Elder
@ 2021-06-11 19:05 ` Alex Elder
  2021-06-11 19:05 ` [PATCH net-next 4/8] net: qualcomm: rmnet: simplify rmnet_map_get_csum_field() Alex Elder
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Alex Elder @ 2021-06-11 19:05 UTC (permalink / raw)
  To: subashab, stranche, davem, kuba; +Cc: bjorn.andersson, netdev, linux-kernel

The value passed as an argument to rmnet_map_ipv4_ul_csum_header()
is always an IPv4 header.  Rather than using a local variable, just
have the type of the argument reflect the proper type.

In rmnet_map_ipv6_ul_csum_header() things are defined a little
differently, but make the same basic change there.

Signed-off-by: Alex Elder <elder@linaro.org>
---
 drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
index b8e504ac7fb1e..ca07b87d7ed71 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
@@ -195,15 +195,14 @@ static void rmnet_map_complement_ipv4_txporthdr_csum_field(void *iphdr)
 }
 
 static void
-rmnet_map_ipv4_ul_csum_header(void *iphdr,
+rmnet_map_ipv4_ul_csum_header(struct iphdr *iphdr,
 			      struct rmnet_map_ul_csum_header *ul_header,
 			      struct sk_buff *skb)
 {
-	struct iphdr *ip4h = iphdr;
 	u16 val;
 
 	val = MAP_CSUM_UL_ENABLED_FLAG;
-	if (ip4h->protocol == IPPROTO_UDP)
+	if (iphdr->protocol == IPPROTO_UDP)
 		val |= MAP_CSUM_UL_UDP_FLAG;
 	val |= skb->csum_offset & MAP_CSUM_UL_OFFSET_MASK;
 
@@ -231,15 +230,14 @@ static void rmnet_map_complement_ipv6_txporthdr_csum_field(void *ip6hdr)
 }
 
 static void
-rmnet_map_ipv6_ul_csum_header(void *ip6hdr,
+rmnet_map_ipv6_ul_csum_header(struct ipv6hdr *ipv6hdr,
 			      struct rmnet_map_ul_csum_header *ul_header,
 			      struct sk_buff *skb)
 {
-	struct ipv6hdr *ip6h = ip6hdr;
 	u16 val;
 
 	val = MAP_CSUM_UL_ENABLED_FLAG;
-	if (ip6h->nexthdr == IPPROTO_UDP)
+	if (ipv6hdr->nexthdr == IPPROTO_UDP)
 		val |= MAP_CSUM_UL_UDP_FLAG;
 	val |= skb->csum_offset & MAP_CSUM_UL_OFFSET_MASK;
 
@@ -248,7 +246,7 @@ rmnet_map_ipv6_ul_csum_header(void *ip6hdr,
 
 	skb->ip_summed = CHECKSUM_NONE;
 
-	rmnet_map_complement_ipv6_txporthdr_csum_field(ip6hdr);
+	rmnet_map_complement_ipv6_txporthdr_csum_field(ipv6hdr);
 }
 #endif
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH net-next 4/8] net: qualcomm: rmnet: simplify rmnet_map_get_csum_field()
  2021-06-11 19:05 [PATCH net-next 0/8] net: qualcomm: rmnet: MAPv4 download checksum cleanup, part 1 Alex Elder
                   ` (2 preceding siblings ...)
  2021-06-11 19:05 ` [PATCH net-next 3/8] net: qualcomm: rmnet: get rid of some local variables Alex Elder
@ 2021-06-11 19:05 ` Alex Elder
  2021-06-11 19:05 ` [PATCH net-next 5/8] net: qualcomm: rmnet: IPv4 header has zero checksum Alex Elder
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Alex Elder @ 2021-06-11 19:05 UTC (permalink / raw)
  To: subashab, stranche, davem, kuba; +Cc: bjorn.andersson, netdev, linux-kernel

The checksum fields of the TCP and UDP header structures already
have type __sum16.  We don't support any other protocol headers, so
we can simplify rmnet_map_get_csum_field(), getting rid of the local
variable entirely and just returning the appropriate address.

Signed-off-by: Alex Elder <elder@linaro.org>
---
 .../ethernet/qualcomm/rmnet/rmnet_map_data.c  | 20 +++++--------------
 1 file changed, 5 insertions(+), 15 deletions(-)

diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
index ca07b87d7ed71..79f1d516b5cca 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
@@ -19,23 +19,13 @@
 static __sum16 *rmnet_map_get_csum_field(unsigned char protocol,
 					 const void *txporthdr)
 {
-	__sum16 *check = NULL;
+	if (protocol == IPPROTO_TCP)
+		return &((struct tcphdr *)txporthdr)->check;
 
-	switch (protocol) {
-	case IPPROTO_TCP:
-		check = &(((struct tcphdr *)txporthdr)->check);
-		break;
+	if (protocol == IPPROTO_UDP)
+		return &((struct udphdr *)txporthdr)->check;
 
-	case IPPROTO_UDP:
-		check = &(((struct udphdr *)txporthdr)->check);
-		break;
-
-	default:
-		check = NULL;
-		break;
-	}
-
-	return check;
+	return NULL;
 }
 
 static int
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH net-next 5/8] net: qualcomm: rmnet: IPv4 header has zero checksum
  2021-06-11 19:05 [PATCH net-next 0/8] net: qualcomm: rmnet: MAPv4 download checksum cleanup, part 1 Alex Elder
                   ` (3 preceding siblings ...)
  2021-06-11 19:05 ` [PATCH net-next 4/8] net: qualcomm: rmnet: simplify rmnet_map_get_csum_field() Alex Elder
@ 2021-06-11 19:05 ` Alex Elder
  2021-06-11 19:05 ` [PATCH net-next 6/8] net: qualcomm: rmnet: clarify a bit of code Alex Elder
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Alex Elder @ 2021-06-11 19:05 UTC (permalink / raw)
  To: subashab, stranche, davem, kuba; +Cc: bjorn.andersson, netdev, linux-kernel

In rmnet_map_ipv4_dl_csum_trailer(), an illegal checksum subtraction
is done, subtracting hdr_csum (in host byte order) from csum_value (in
network byte order).  Despite being illegal, it generally works,
because it turns out the value subtracted is (or should be) always 0,
which has the same representation in either byte order.

Doing illegal operations is not good form though, so fix this by
verifying the IP header checksum early in that function.  If its
checksum is non-zero, the packet will be bad, so just return an
error.  This will cause the packet to passed to the IP layer where
it can be dropped.

Thereafter, there is no need subtract the IP header checksum from
the checksum value in the trailer because we know it is zero.
Add a comment explaining this.

This type of packet error is different from other types, so add a
new statistics counter to track this condition.

Signed-off-by: Alex Elder <elder@linaro.org>
---
 .../ethernet/qualcomm/rmnet/rmnet_config.h    |  1 +
 .../ethernet/qualcomm/rmnet/rmnet_map_data.c  | 41 ++++++++++++-------
 .../net/ethernet/qualcomm/rmnet/rmnet_vnd.c   |  1 +
 3 files changed, 29 insertions(+), 14 deletions(-)

diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
index 8e64ca98068d9..3d3cba56c5169 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
@@ -49,6 +49,7 @@ struct rmnet_pcpu_stats {
 
 struct rmnet_priv_stats {
 	u64 csum_ok;
+	u64 csum_ip4_header_bad;
 	u64 csum_valid_unset;
 	u64 csum_validation_failed;
 	u64 csum_err_bad_buffer;
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
index 79f1d516b5cca..40d7e0c615f9c 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
@@ -33,13 +33,21 @@ rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb,
 			       struct rmnet_map_dl_csum_trailer *csum_trailer,
 			       struct rmnet_priv *priv)
 {
-	__sum16 *csum_field, csum_temp, pseudo_csum, hdr_csum, ip_payload_csum;
-	u16 csum_value, csum_value_final;
-	struct iphdr *ip4h;
-	void *txporthdr;
+	struct iphdr *ip4h = (struct iphdr *)skb->data;
+	void *txporthdr = skb->data + ip4h->ihl * 4;
+	__sum16 *csum_field, csum_temp, pseudo_csum;
+	__sum16 ip_payload_csum;
+	u16 csum_value_final;
 	__be16 addend;
 
-	ip4h = (struct iphdr *)(skb->data);
+	/* Computing the checksum over just the IPv4 header--including its
+	 * checksum field--should yield 0.  If it doesn't, the IP header
+	 * is bad, so return an error and let the IP layer drop it.
+	 */
+	if (ip_fast_csum(ip4h, ip4h->ihl)) {
+		priv->stats.csum_ip4_header_bad++;
+		return -EINVAL;
+	}
 
 	/* We don't support checksum offload on IPv4 fragments */
 	if (ip_is_fragment(ip4h)) {
@@ -47,25 +55,30 @@ rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb,
 		return -EOPNOTSUPP;
 	}
 
-	txporthdr = skb->data + ip4h->ihl * 4;
-
+	/* Checksum offload is only supported for UDP and TCP protocols */
 	csum_field = rmnet_map_get_csum_field(ip4h->protocol, txporthdr);
-
 	if (!csum_field) {
 		priv->stats.csum_err_invalid_transport++;
 		return -EPROTONOSUPPORT;
 	}
 
-	/* RFC 768 - Skip IPv4 UDP packets where sender checksum field is 0 */
-	if (*csum_field == 0 && ip4h->protocol == IPPROTO_UDP) {
+	/* RFC 768: UDP checksum is optional for IPv4, and is 0 if unused */
+	if (!*csum_field && ip4h->protocol == IPPROTO_UDP) {
 		priv->stats.csum_skipped++;
 		return 0;
 	}
 
-	csum_value = ~ntohs(csum_trailer->csum_value);
-	hdr_csum = ~ip_fast_csum(ip4h, (int)ip4h->ihl);
-	ip_payload_csum = csum16_sub((__force __sum16)csum_value,
-				     (__force __be16)hdr_csum);
+	/* The checksum value in the trailer is computed over the entire
+	 * IP packet, including the IP header and payload.  To derive the
+	 * transport checksum from this, we first subract the contribution
+	 * of the IP header from the trailer checksum.  We then add the
+	 * checksum computed over the pseudo header.
+	 *
+	 * We verified above that the IP header contributes zero to the
+	 * trailer checksum.  Therefore the checksum in the trailer is
+	 * just the checksum computed over the IP payload.
+	 */
+	ip_payload_csum = (__force __sum16)~ntohs(csum_trailer->csum_value);
 
 	pseudo_csum = ~csum_tcpudp_magic(ip4h->saddr, ip4h->daddr,
 					 ntohs(ip4h->tot_len) - ip4h->ihl * 4,
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
index fe13017e9a41e..6556b5381ce85 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c
@@ -166,6 +166,7 @@ static const struct net_device_ops rmnet_vnd_ops = {
 
 static const char rmnet_gstrings_stats[][ETH_GSTRING_LEN] = {
 	"Checksum ok",
+	"Bad IPv4 header checksum",
 	"Checksum valid bit not set",
 	"Checksum validation failed",
 	"Checksum error bad buffer",
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH net-next 6/8] net: qualcomm: rmnet: clarify a bit of code
  2021-06-11 19:05 [PATCH net-next 0/8] net: qualcomm: rmnet: MAPv4 download checksum cleanup, part 1 Alex Elder
                   ` (4 preceding siblings ...)
  2021-06-11 19:05 ` [PATCH net-next 5/8] net: qualcomm: rmnet: IPv4 header has zero checksum Alex Elder
@ 2021-06-11 19:05 ` Alex Elder
  2021-06-11 19:05 ` [PATCH net-next 7/8] net: qualcomm: rmnet: avoid unnecessary byte-swapping Alex Elder
  2021-06-11 19:05 ` [PATCH net-next 8/8] net: qualcomm: rmnet: avoid unnecessary IPv6 byte-swapping Alex Elder
  7 siblings, 0 replies; 9+ messages in thread
From: Alex Elder @ 2021-06-11 19:05 UTC (permalink / raw)
  To: subashab, stranche, davem, kuba; +Cc: bjorn.andersson, netdev, linux-kernel

In rmnet_map_ipv6_dl_csum_trailer() there is an especially involved
line of code that determines the ones' complement sum of the IPv6
packet header (in host byte order).  Simplify that by storing the
result of computing just the header checksum in a local variable,
then using that in the original assignment.

Use the size of the IPv6 header structure as the number of bytes to
checksum, rather than computing the offset to the transport header.
And use ip_fast_csum() rather than ipa_compute_csum(), knowing that
the size of an IPv6 header (40 bytes) is a multiple of 4 bytes
greater than 16.

Add some comments to match rmnet_map_ipv4_dl_csum_trailer().

Signed-off-by: Alex Elder <elder@linaro.org>
---
 .../ethernet/qualcomm/rmnet/rmnet_map_data.c  | 26 ++++++++++++-------
 1 file changed, 16 insertions(+), 10 deletions(-)

diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
index 40d7e0c615f9c..4f93355e9a93a 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
@@ -120,27 +120,33 @@ rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb,
 			       struct rmnet_map_dl_csum_trailer *csum_trailer,
 			       struct rmnet_priv *priv)
 {
-	__sum16 *csum_field, ip6_payload_csum, pseudo_csum, csum_temp;
+	struct ipv6hdr *ip6h = (struct ipv6hdr *)skb->data;
+	void *txporthdr = skb->data + sizeof(*ip6h);
+	__sum16 *csum_field, pseudo_csum, csum_temp;
 	u16 csum_value, csum_value_final;
 	__be16 ip6_hdr_csum, addend;
-	struct ipv6hdr *ip6h;
-	void *txporthdr;
+	__sum16 ip6_payload_csum;
+	__be16 ip_header_csum;
 	u32 length;
 
-	ip6h = (struct ipv6hdr *)(skb->data);
-
-	txporthdr = skb->data + sizeof(struct ipv6hdr);
+	/* Checksum offload is only supported for UDP and TCP protocols;
+	 * the packet cannot include any IPv6 extension headers
+	 */
 	csum_field = rmnet_map_get_csum_field(ip6h->nexthdr, txporthdr);
-
 	if (!csum_field) {
 		priv->stats.csum_err_invalid_transport++;
 		return -EPROTONOSUPPORT;
 	}
 
+	/* The checksum value in the trailer is computed over the entire
+	 * IP packet, including the IP header and payload.  To derive the
+	 * transport checksum from this, we first subract the contribution
+	 * of the IP header from the trailer checksum.  We then add the
+	 * checksum computed over the pseudo header.
+	 */
 	csum_value = ~ntohs(csum_trailer->csum_value);
-	ip6_hdr_csum = (__force __be16)
-			~ntohs((__force __be16)ip_compute_csum(ip6h,
-			       (int)(txporthdr - (void *)(skb->data))));
+	ip_header_csum = (__force __be16)ip_fast_csum(ip6h, sizeof(*ip6h) / 4);
+	ip6_hdr_csum = (__force __be16)~ntohs(ip_header_csum);
 	ip6_payload_csum = csum16_sub((__force __sum16)csum_value,
 				      ip6_hdr_csum);
 
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH net-next 7/8] net: qualcomm: rmnet: avoid unnecessary byte-swapping
  2021-06-11 19:05 [PATCH net-next 0/8] net: qualcomm: rmnet: MAPv4 download checksum cleanup, part 1 Alex Elder
                   ` (5 preceding siblings ...)
  2021-06-11 19:05 ` [PATCH net-next 6/8] net: qualcomm: rmnet: clarify a bit of code Alex Elder
@ 2021-06-11 19:05 ` Alex Elder
  2021-06-11 19:05 ` [PATCH net-next 8/8] net: qualcomm: rmnet: avoid unnecessary IPv6 byte-swapping Alex Elder
  7 siblings, 0 replies; 9+ messages in thread
From: Alex Elder @ 2021-06-11 19:05 UTC (permalink / raw)
  To: subashab, stranche, davem, kuba; +Cc: bjorn.andersson, netdev, linux-kernel

Internet checksums are used for IPv4 header checksum, as well as TCP
segment and UDP datagram checksums.  Such a checksum represents the
negated sum of adjacent pairs of bytes, using ones' complement
arithmetic.

One property of the Internet checkum is byte order independence [1].
Specifically, the sum of byte-swapped pairs is equal to the result
of byte swapping the sum of those same pairs when not byte-swapped.

So for example if a, b, c, d, y, and z are hexadecimal digits, and
PLUS represents ones' complement addition:
    If:		ab PLUS cd = yz
    Then:	ba PLUS dc = zy

For this reason, there is no need to swap the order of bytes in the
checksum value held in a message header, nor the one in the QMAPv4
trailer, in order to operate on them.

In other words, we can determine whether the hardware-computed
checksum matches the one in the message header without any byte
swaps.

(This patch leaves in place all existing type casts.)

[1] https://tools.ietf.org/html/rfc1071

Signed-off-by: Alex Elder <elder@linaro.org>
---
 drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
index 4f93355e9a93a..39f198d7595bd 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
@@ -78,15 +78,15 @@ rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb,
 	 * trailer checksum.  Therefore the checksum in the trailer is
 	 * just the checksum computed over the IP payload.
 	 */
-	ip_payload_csum = (__force __sum16)~ntohs(csum_trailer->csum_value);
+	ip_payload_csum = (__force __sum16)~csum_trailer->csum_value;
 
 	pseudo_csum = ~csum_tcpudp_magic(ip4h->saddr, ip4h->daddr,
 					 ntohs(ip4h->tot_len) - ip4h->ihl * 4,
 					 ip4h->protocol, 0);
-	addend = (__force __be16)ntohs((__force __be16)pseudo_csum);
+	addend = (__force __be16)pseudo_csum;
 	pseudo_csum = csum16_add(ip_payload_csum, addend);
 
-	addend = (__force __be16)ntohs((__force __be16)*csum_field);
+	addend = (__force __be16)*csum_field;
 	csum_temp = ~csum16_sub(pseudo_csum, addend);
 	csum_value_final = (__force u16)csum_temp;
 
@@ -105,7 +105,7 @@ rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb,
 		}
 	}
 
-	if (csum_value_final == ntohs((__force __be16)*csum_field)) {
+	if (csum_value_final == (__force u16)*csum_field) {
 		priv->stats.csum_ok++;
 		return 0;
 	} else {
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH net-next 8/8] net: qualcomm: rmnet: avoid unnecessary IPv6 byte-swapping
  2021-06-11 19:05 [PATCH net-next 0/8] net: qualcomm: rmnet: MAPv4 download checksum cleanup, part 1 Alex Elder
                   ` (6 preceding siblings ...)
  2021-06-11 19:05 ` [PATCH net-next 7/8] net: qualcomm: rmnet: avoid unnecessary byte-swapping Alex Elder
@ 2021-06-11 19:05 ` Alex Elder
  7 siblings, 0 replies; 9+ messages in thread
From: Alex Elder @ 2021-06-11 19:05 UTC (permalink / raw)
  To: subashab, stranche, davem, kuba; +Cc: bjorn.andersson, netdev, linux-kernel

In the previous patch IPv4 download checksum offload code was
updated to avoid unnecessary byte swapping, based on properties of
the Internet checksum algorithm.  This patch makes comparable
changes to the IPv6 download checksum offload handling.

Signed-off-by: Alex Elder <elder@linaro.org>
---
 .../ethernet/qualcomm/rmnet/rmnet_map_data.c    | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
index 39f198d7595bd..d4d23ab446ef5 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
@@ -123,10 +123,11 @@ rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb,
 	struct ipv6hdr *ip6h = (struct ipv6hdr *)skb->data;
 	void *txporthdr = skb->data + sizeof(*ip6h);
 	__sum16 *csum_field, pseudo_csum, csum_temp;
-	u16 csum_value, csum_value_final;
 	__be16 ip6_hdr_csum, addend;
 	__sum16 ip6_payload_csum;
 	__be16 ip_header_csum;
+	u16 csum_value_final;
+	__be16 csum_value;
 	u32 length;
 
 	/* Checksum offload is only supported for UDP and TCP protocols;
@@ -144,21 +145,21 @@ rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb,
 	 * of the IP header from the trailer checksum.  We then add the
 	 * checksum computed over the pseudo header.
 	 */
-	csum_value = ~ntohs(csum_trailer->csum_value);
+	csum_value = ~csum_trailer->csum_value;
 	ip_header_csum = (__force __be16)ip_fast_csum(ip6h, sizeof(*ip6h) / 4);
-	ip6_hdr_csum = (__force __be16)~ntohs(ip_header_csum);
+	ip6_hdr_csum = (__force __be16)~ip_header_csum;
 	ip6_payload_csum = csum16_sub((__force __sum16)csum_value,
 				      ip6_hdr_csum);
 
 	length = (ip6h->nexthdr == IPPROTO_UDP) ?
 		 ntohs(((struct udphdr *)txporthdr)->len) :
 		 ntohs(ip6h->payload_len);
-	pseudo_csum = ~(csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
-			     length, ip6h->nexthdr, 0));
-	addend = (__force __be16)ntohs((__force __be16)pseudo_csum);
+	pseudo_csum = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
+				       length, ip6h->nexthdr, 0);
+	addend = (__force __be16)pseudo_csum;
 	pseudo_csum = csum16_add(ip6_payload_csum, addend);
 
-	addend = (__force __be16)ntohs((__force __be16)*csum_field);
+	addend = (__force __be16)*csum_field;
 	csum_temp = ~csum16_sub(pseudo_csum, addend);
 	csum_value_final = (__force u16)csum_temp;
 
@@ -179,7 +180,7 @@ rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb,
 		}
 	}
 
-	if (csum_value_final == ntohs((__force __be16)*csum_field)) {
+	if (csum_value_final == (__force u16)*csum_field) {
 		priv->stats.csum_ok++;
 		return 0;
 	} else {
-- 
2.27.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2021-06-11 19:07 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-11 19:05 [PATCH net-next 0/8] net: qualcomm: rmnet: MAPv4 download checksum cleanup, part 1 Alex Elder
2021-06-11 19:05 ` [PATCH net-next 1/8] net: qualcomm: rmnet: use ip_is_fragment() Alex Elder
2021-06-11 19:05 ` [PATCH net-next 2/8] net: qualcomm: rmnet: eliminate some ifdefs Alex Elder
2021-06-11 19:05 ` [PATCH net-next 3/8] net: qualcomm: rmnet: get rid of some local variables Alex Elder
2021-06-11 19:05 ` [PATCH net-next 4/8] net: qualcomm: rmnet: simplify rmnet_map_get_csum_field() Alex Elder
2021-06-11 19:05 ` [PATCH net-next 5/8] net: qualcomm: rmnet: IPv4 header has zero checksum Alex Elder
2021-06-11 19:05 ` [PATCH net-next 6/8] net: qualcomm: rmnet: clarify a bit of code Alex Elder
2021-06-11 19:05 ` [PATCH net-next 7/8] net: qualcomm: rmnet: avoid unnecessary byte-swapping Alex Elder
2021-06-11 19:05 ` [PATCH net-next 8/8] net: qualcomm: rmnet: avoid unnecessary IPv6 byte-swapping Alex Elder

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.