Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

octeontx2-pf: Recalculate UDP checksum for ptp 1-step sync packet

When checksum offload is disabled in the driver via ethtool,
the PTP 1-step sync packets contain incorrect checksum, since
the stack calculates the checksum before driver updates
PTP timestamp field in the packet. This results in PTP packets
getting dropped at the other end. This patch fixes the issue by
re-calculating the UDP checksum after updating PTP
timestamp field in the driver.

Fixes: 2958d17a8984 ("octeontx2-pf: Add support for ptp 1-step mode on CN10K silicon")
Signed-off-by: Geetha sowjanya <gakula@marvell.com>
Signed-off-by: Hariprasad Kelam <hkelam@marvell.com>
Signed-off-by: Sunil Kovvuri Goutham <sgoutham@marvell.com>
Signed-off-by: Sai Krishna <saikrishnag@marvell.com>
Link: https://lore.kernel.org/r/20230222113600.1965116-1-saikrishnag@marvell.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Geetha sowjanya and committed by
Paolo Abeni
edea0c5a 1e30373e

+57 -19
+57 -19
drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c
··· 10 10 #include <net/tso.h> 11 11 #include <linux/bpf.h> 12 12 #include <linux/bpf_trace.h> 13 + #include <net/ip6_checksum.h> 13 14 14 15 #include "otx2_reg.h" 15 16 #include "otx2_common.h" ··· 700 699 701 700 static void otx2_sqe_add_mem(struct otx2_snd_queue *sq, int *offset, 702 701 int alg, u64 iova, int ptp_offset, 703 - u64 base_ns, int udp_csum) 702 + u64 base_ns, bool udp_csum_crt) 704 703 { 705 704 struct nix_sqe_mem_s *mem; 706 705 ··· 712 711 713 712 if (ptp_offset) { 714 713 mem->start_offset = ptp_offset; 715 - mem->udp_csum_crt = udp_csum; 714 + mem->udp_csum_crt = !!udp_csum_crt; 716 715 mem->base_ns = base_ns; 717 716 mem->step_type = 1; 718 717 } ··· 987 986 return false; 988 987 } 989 988 990 - static bool otx2_ptp_is_sync(struct sk_buff *skb, int *offset, int *udp_csum) 989 + static bool otx2_ptp_is_sync(struct sk_buff *skb, int *offset, bool *udp_csum_crt) 991 990 { 992 991 struct ethhdr *eth = (struct ethhdr *)(skb->data); 993 992 u16 nix_offload_hlen = 0, inner_vhlen = 0; 993 + bool udp_hdr_present = false, is_sync; 994 994 u8 *data = skb->data, *msgtype; 995 995 __be16 proto = eth->h_proto; 996 996 int network_depth = 0; ··· 1031 1029 if (!otx2_validate_network_transport(skb)) 1032 1030 return false; 1033 1031 1034 - *udp_csum = 1; 1035 1032 *offset = nix_offload_hlen + skb_transport_offset(skb) + 1036 1033 sizeof(struct udphdr); 1034 + udp_hdr_present = true; 1035 + 1037 1036 } 1038 1037 1039 1038 msgtype = data + *offset; 1040 - 1041 1039 /* Check PTP messageId is SYNC or not */ 1042 - return (*msgtype & 0xf) == 0; 1040 + is_sync = !(*msgtype & 0xf); 1041 + if (is_sync) 1042 + *udp_csum_crt = udp_hdr_present; 1043 + else 1044 + *offset = 0; 1045 + 1046 + return is_sync; 1043 1047 } 1044 1048 1045 1049 static void otx2_set_txtstamp(struct otx2_nic *pfvf, struct sk_buff *skb, 1046 1050 struct otx2_snd_queue *sq, int *offset) 1047 1051 { 1052 + struct ethhdr *eth = (struct ethhdr *)(skb->data); 1048 1053 struct ptpv2_tstamp *origin_tstamp; 1049 - int ptp_offset = 0, udp_csum = 0; 1054 + bool udp_csum_crt = false; 1055 + unsigned int udphoff; 1050 1056 struct timespec64 ts; 1057 + int ptp_offset = 0; 1058 + __wsum skb_csum; 1051 1059 u64 iova; 1052 1060 1053 1061 if (unlikely(!skb_shinfo(skb)->gso_size && 1054 1062 (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))) { 1055 - if (unlikely(pfvf->flags & OTX2_FLAG_PTP_ONESTEP_SYNC)) { 1056 - if (otx2_ptp_is_sync(skb, &ptp_offset, &udp_csum)) { 1057 - origin_tstamp = (struct ptpv2_tstamp *) 1058 - ((u8 *)skb->data + ptp_offset + 1059 - PTP_SYNC_SEC_OFFSET); 1060 - ts = ns_to_timespec64(pfvf->ptp->tstamp); 1061 - origin_tstamp->seconds_msb = htons((ts.tv_sec >> 32) & 0xffff); 1062 - origin_tstamp->seconds_lsb = htonl(ts.tv_sec & 0xffffffff); 1063 - origin_tstamp->nanoseconds = htonl(ts.tv_nsec); 1064 - /* Point to correction field in PTP packet */ 1065 - ptp_offset += 8; 1063 + if (unlikely(pfvf->flags & OTX2_FLAG_PTP_ONESTEP_SYNC && 1064 + otx2_ptp_is_sync(skb, &ptp_offset, &udp_csum_crt))) { 1065 + origin_tstamp = (struct ptpv2_tstamp *) 1066 + ((u8 *)skb->data + ptp_offset + 1067 + PTP_SYNC_SEC_OFFSET); 1068 + ts = ns_to_timespec64(pfvf->ptp->tstamp); 1069 + origin_tstamp->seconds_msb = htons((ts.tv_sec >> 32) & 0xffff); 1070 + origin_tstamp->seconds_lsb = htonl(ts.tv_sec & 0xffffffff); 1071 + origin_tstamp->nanoseconds = htonl(ts.tv_nsec); 1072 + /* Point to correction field in PTP packet */ 1073 + ptp_offset += 8; 1074 + 1075 + /* When user disables hw checksum, stack calculates the csum, 1076 + * but it does not cover ptp timestamp which is added later. 1077 + * Recalculate the checksum manually considering the timestamp. 1078 + */ 1079 + if (udp_csum_crt) { 1080 + struct udphdr *uh = udp_hdr(skb); 1081 + 1082 + if (skb->ip_summed != CHECKSUM_PARTIAL && uh->check != 0) { 1083 + udphoff = skb_transport_offset(skb); 1084 + uh->check = 0; 1085 + skb_csum = skb_checksum(skb, udphoff, skb->len - udphoff, 1086 + 0); 1087 + if (ntohs(eth->h_proto) == ETH_P_IPV6) 1088 + uh->check = csum_ipv6_magic(&ipv6_hdr(skb)->saddr, 1089 + &ipv6_hdr(skb)->daddr, 1090 + skb->len - udphoff, 1091 + ipv6_hdr(skb)->nexthdr, 1092 + skb_csum); 1093 + else 1094 + uh->check = csum_tcpudp_magic(ip_hdr(skb)->saddr, 1095 + ip_hdr(skb)->daddr, 1096 + skb->len - udphoff, 1097 + IPPROTO_UDP, 1098 + skb_csum); 1099 + } 1066 1100 } 1067 1101 } else { 1068 1102 skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 1069 1103 } 1070 1104 iova = sq->timestamps->iova + (sq->head * sizeof(u64)); 1071 1105 otx2_sqe_add_mem(sq, offset, NIX_SENDMEMALG_E_SETTSTMP, iova, 1072 - ptp_offset, pfvf->ptp->base_ns, udp_csum); 1106 + ptp_offset, pfvf->ptp->base_ns, udp_csum_crt); 1073 1107 } else { 1074 1108 skb_tx_timestamp(skb); 1075 1109 }