Loading drivers/net/ethernet/qualcomm/rmnet/rmnet_descriptor.c +42 −0 Original line number Diff line number Diff line Loading @@ -626,6 +626,33 @@ static void __rmnet_frag_segment_data(struct rmnet_frag_descriptor *coal_desc, list_add_tail(&new_frag->list, list); } static bool rmnet_frag_validate_csum(struct rmnet_frag_descriptor *frag_desc) { u8 *data = rmnet_frag_data_ptr(frag_desc); unsigned int datagram_len; __wsum csum; __sum16 pseudo; datagram_len = skb_frag_size(&frag_desc->frag) - frag_desc->ip_len; if (frag_desc->ip_proto == 4) { struct iphdr *iph = (struct iphdr *)data; pseudo = ~csum_tcpudp_magic(iph->saddr, iph->daddr, datagram_len, frag_desc->trans_proto, 0); } else { struct ipv6hdr *ip6h = (struct ipv6hdr *)data; pseudo = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, datagram_len, frag_desc->trans_proto, 0); } csum = csum_partial(data + frag_desc->ip_len, datagram_len, csum_unfold(pseudo)); return !csum_fold(csum); } /* Converts the coalesced frame into a list of descriptors. * NLOs containing csum erros will not be included. */ Loading Loading @@ -708,6 +735,21 @@ rmnet_frag_segment_coal_data(struct rmnet_frag_descriptor *coal_desc, } coal_desc->hdrs_valid = 1; if (rmnet_map_v5_csum_buggy(coal_hdr)) { /* Mark the checksum as valid if it checks out */ if (rmnet_frag_validate_csum(coal_desc)) coal_desc->csum_valid = true; coal_desc->hdr_ptr = rmnet_frag_data_ptr(coal_desc); coal_desc->gso_size = ntohs(coal_hdr->nl_pairs[0].pkt_len); coal_desc->gso_size -= coal_desc->ip_len + coal_desc->trans_len; coal_desc->gso_segs = coal_hdr->nl_pairs[0].num_packets; list_add_tail(&coal_desc->list, list); return; } /* Segment the coalesced descriptor into new packets */ for (nlo = 0; nlo < coal_hdr->num_nlos; nlo++) { pkt_len = ntohs(coal_hdr->nl_pairs[nlo].pkt_len); pkt_len -= coal_desc->ip_len + coal_desc->trans_len; Loading drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h +1 −0 Original line number Diff line number Diff line Loading @@ -253,6 +253,7 @@ int rmnet_map_checksum_downlink_packet(struct sk_buff *skb, u16 len); void rmnet_map_checksum_uplink_packet(struct sk_buff *skb, struct net_device *orig_dev, int csum_type); bool rmnet_map_v5_csum_buggy(struct rmnet_map_v5_coal_header *coal_hdr); int rmnet_map_process_next_hdr_packet(struct sk_buff *skb, struct sk_buff_head *list, u16 len); Loading drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c +62 −0 Original line number Diff line number Diff line Loading @@ -542,6 +542,29 @@ void rmnet_map_checksum_uplink_packet(struct sk_buff *skb, } } bool rmnet_map_v5_csum_buggy(struct rmnet_map_v5_coal_header *coal_hdr) { /* Only applies to frames with a single packet */ if (coal_hdr->num_nlos != 1 || coal_hdr->nl_pairs[0].num_packets != 1) return false; /* TCP header has FIN or PUSH set */ if (coal_hdr->close_type == RMNET_MAP_COAL_CLOSE_COAL) return true; /* Hit packet limit, byte limit, or time limit/EOF on DMA */ if (coal_hdr->close_type == RMNET_MAP_COAL_CLOSE_HW) { switch (coal_hdr->close_value) { case RMNET_MAP_COAL_CLOSE_HW_PKT: case RMNET_MAP_COAL_CLOSE_HW_BYTE: case RMNET_MAP_COAL_CLOSE_HW_TIME: return true; } } return false; } static void rmnet_map_move_headers(struct sk_buff *skb) { struct iphdr *iph; Loading Loading @@ -734,6 +757,34 @@ __rmnet_map_segment_coal_skb(struct sk_buff *coal_skb, coal_meta->pkt_count = 0; } static bool rmnet_map_validate_csum(struct sk_buff *skb, struct rmnet_map_coal_metadata *meta) { u8 *data = rmnet_map_data_ptr(skb); unsigned int datagram_len; __wsum csum; __sum16 pseudo; datagram_len = skb->len - meta->ip_len; if (meta->ip_proto == 4) { struct iphdr *iph = (struct iphdr *)data; pseudo = ~csum_tcpudp_magic(iph->saddr, iph->daddr, datagram_len, meta->trans_proto, 0); } else { struct ipv6hdr *ip6h = (struct ipv6hdr *)data; pseudo = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, datagram_len, meta->trans_proto, 0); } csum = skb_checksum(skb, meta->ip_len, datagram_len, csum_unfold(pseudo)); return !csum_fold(csum); } /* Converts the coalesced SKB into a list of SKBs. * NLOs containing csum erros will not be included. * The original coalesced SKB should be treated as invalid and Loading Loading @@ -817,6 +868,17 @@ static void rmnet_map_segment_coal_skb(struct sk_buff *coal_skb, return; } if (rmnet_map_v5_csum_buggy(coal_hdr)) { rmnet_map_move_headers(coal_skb); /* Mark as valid if it checks out */ if (rmnet_map_validate_csum(coal_skb, &coal_meta)) coal_skb->ip_summed = CHECKSUM_UNNECESSARY; __skb_queue_tail(list, coal_skb); return; } /* Segment the coalesced SKB into new packets */ for (nlo = 0; nlo < coal_hdr->num_nlos; nlo++) { pkt_len = ntohs(coal_hdr->nl_pairs[nlo].pkt_len); pkt_len -= coal_meta.ip_len + coal_meta.trans_len; Loading Loading
drivers/net/ethernet/qualcomm/rmnet/rmnet_descriptor.c +42 −0 Original line number Diff line number Diff line Loading @@ -626,6 +626,33 @@ static void __rmnet_frag_segment_data(struct rmnet_frag_descriptor *coal_desc, list_add_tail(&new_frag->list, list); } static bool rmnet_frag_validate_csum(struct rmnet_frag_descriptor *frag_desc) { u8 *data = rmnet_frag_data_ptr(frag_desc); unsigned int datagram_len; __wsum csum; __sum16 pseudo; datagram_len = skb_frag_size(&frag_desc->frag) - frag_desc->ip_len; if (frag_desc->ip_proto == 4) { struct iphdr *iph = (struct iphdr *)data; pseudo = ~csum_tcpudp_magic(iph->saddr, iph->daddr, datagram_len, frag_desc->trans_proto, 0); } else { struct ipv6hdr *ip6h = (struct ipv6hdr *)data; pseudo = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, datagram_len, frag_desc->trans_proto, 0); } csum = csum_partial(data + frag_desc->ip_len, datagram_len, csum_unfold(pseudo)); return !csum_fold(csum); } /* Converts the coalesced frame into a list of descriptors. * NLOs containing csum erros will not be included. */ Loading Loading @@ -708,6 +735,21 @@ rmnet_frag_segment_coal_data(struct rmnet_frag_descriptor *coal_desc, } coal_desc->hdrs_valid = 1; if (rmnet_map_v5_csum_buggy(coal_hdr)) { /* Mark the checksum as valid if it checks out */ if (rmnet_frag_validate_csum(coal_desc)) coal_desc->csum_valid = true; coal_desc->hdr_ptr = rmnet_frag_data_ptr(coal_desc); coal_desc->gso_size = ntohs(coal_hdr->nl_pairs[0].pkt_len); coal_desc->gso_size -= coal_desc->ip_len + coal_desc->trans_len; coal_desc->gso_segs = coal_hdr->nl_pairs[0].num_packets; list_add_tail(&coal_desc->list, list); return; } /* Segment the coalesced descriptor into new packets */ for (nlo = 0; nlo < coal_hdr->num_nlos; nlo++) { pkt_len = ntohs(coal_hdr->nl_pairs[nlo].pkt_len); pkt_len -= coal_desc->ip_len + coal_desc->trans_len; Loading
drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h +1 −0 Original line number Diff line number Diff line Loading @@ -253,6 +253,7 @@ int rmnet_map_checksum_downlink_packet(struct sk_buff *skb, u16 len); void rmnet_map_checksum_uplink_packet(struct sk_buff *skb, struct net_device *orig_dev, int csum_type); bool rmnet_map_v5_csum_buggy(struct rmnet_map_v5_coal_header *coal_hdr); int rmnet_map_process_next_hdr_packet(struct sk_buff *skb, struct sk_buff_head *list, u16 len); Loading
drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c +62 −0 Original line number Diff line number Diff line Loading @@ -542,6 +542,29 @@ void rmnet_map_checksum_uplink_packet(struct sk_buff *skb, } } bool rmnet_map_v5_csum_buggy(struct rmnet_map_v5_coal_header *coal_hdr) { /* Only applies to frames with a single packet */ if (coal_hdr->num_nlos != 1 || coal_hdr->nl_pairs[0].num_packets != 1) return false; /* TCP header has FIN or PUSH set */ if (coal_hdr->close_type == RMNET_MAP_COAL_CLOSE_COAL) return true; /* Hit packet limit, byte limit, or time limit/EOF on DMA */ if (coal_hdr->close_type == RMNET_MAP_COAL_CLOSE_HW) { switch (coal_hdr->close_value) { case RMNET_MAP_COAL_CLOSE_HW_PKT: case RMNET_MAP_COAL_CLOSE_HW_BYTE: case RMNET_MAP_COAL_CLOSE_HW_TIME: return true; } } return false; } static void rmnet_map_move_headers(struct sk_buff *skb) { struct iphdr *iph; Loading Loading @@ -734,6 +757,34 @@ __rmnet_map_segment_coal_skb(struct sk_buff *coal_skb, coal_meta->pkt_count = 0; } static bool rmnet_map_validate_csum(struct sk_buff *skb, struct rmnet_map_coal_metadata *meta) { u8 *data = rmnet_map_data_ptr(skb); unsigned int datagram_len; __wsum csum; __sum16 pseudo; datagram_len = skb->len - meta->ip_len; if (meta->ip_proto == 4) { struct iphdr *iph = (struct iphdr *)data; pseudo = ~csum_tcpudp_magic(iph->saddr, iph->daddr, datagram_len, meta->trans_proto, 0); } else { struct ipv6hdr *ip6h = (struct ipv6hdr *)data; pseudo = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, datagram_len, meta->trans_proto, 0); } csum = skb_checksum(skb, meta->ip_len, datagram_len, csum_unfold(pseudo)); return !csum_fold(csum); } /* Converts the coalesced SKB into a list of SKBs. * NLOs containing csum erros will not be included. * The original coalesced SKB should be treated as invalid and Loading Loading @@ -817,6 +868,17 @@ static void rmnet_map_segment_coal_skb(struct sk_buff *coal_skb, return; } if (rmnet_map_v5_csum_buggy(coal_hdr)) { rmnet_map_move_headers(coal_skb); /* Mark as valid if it checks out */ if (rmnet_map_validate_csum(coal_skb, &coal_meta)) coal_skb->ip_summed = CHECKSUM_UNNECESSARY; __skb_queue_tail(list, coal_skb); return; } /* Segment the coalesced SKB into new packets */ for (nlo = 0; nlo < coal_hdr->num_nlos; nlo++) { pkt_len = ntohs(coal_hdr->nl_pairs[nlo].pkt_len); pkt_len -= coal_meta.ip_len + coal_meta.trans_len; Loading