Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dcbaf5b9 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "net: qualcomm: rmnet: Workaround for HW checksum error"

parents 7ce67401 b778d6bd
Loading
Loading
Loading
Loading
+42 −0
Original line number Diff line number Diff line
@@ -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.
 */
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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);
+62 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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
@@ -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;