Browse Source

qcacmn: Restrict each tso segment to 6 frags max

HW has the limit on the number of fragments per tso
segment of FRAG_NUM_MAX (=6).
When running traffic, network stack may send many small
messages which could result in tso segment has more number
of fragments than 6.
Therefore, ensure that tso segment should not contain
more than 6 fragment even if tso size is less than gso seg size.

CRs-Fixed: 2107317
Change-Id: I08f604e4f7fa6a1d6ab065c2360bba883328e5f2
Poddar, Siddarth 7 years ago
parent
commit
a8cff1d786
1 changed files with 96 additions and 10 deletions
  1. 96 10
      qdf/linux/src/qdf_nbuf.c

+ 96 - 10
qdf/linux/src/qdf_nbuf.c

@@ -2196,6 +2196,18 @@ uint32_t __qdf_nbuf_get_tso_info(qdf_device_t osdev, struct sk_buff *skb,
 					tso_seg_size = tso_seg_size -
 						tso_frag_len;
 					i++;
+					if (curr_seg->seg.num_frags ==
+								FRAG_NUM_MAX) {
+						more_tso_frags = 0;
+						/*
+						 * reset i and the tso
+						 * payload size
+						 */
+						i = 1;
+						tso_seg_size =
+							skb_shinfo(skb)->
+								gso_size;
+					}
 				}
 			} else {
 				more_tso_frags = 0;
@@ -2339,17 +2351,91 @@ EXPORT_SYMBOL(__qdf_nbuf_unmap_tso_segment);
 #ifndef BUILD_X86
 uint32_t __qdf_nbuf_get_tso_num_seg(struct sk_buff *skb)
 {
-	uint32_t gso_size, tmp_len, num_segs = 0;
-
-	gso_size = skb_shinfo(skb)->gso_size;
-	tmp_len = skb->len - ((skb_transport_header(skb) - skb_mac_header(skb))
-		+ tcp_hdrlen(skb));
-	while (tmp_len) {
-		num_segs++;
-		if (tmp_len > gso_size)
-			tmp_len -= gso_size;
+	uint32_t tso_seg_size = skb_shinfo(skb)->gso_size;
+	uint32_t remainder, num_segs = 0;
+	uint8_t skb_nr_frags = skb_shinfo(skb)->nr_frags;
+	uint8_t frags_per_tso = 0;
+	uint32_t skb_frag_len = 0;
+	uint32_t eit_hdr_len = (skb_transport_header(skb)
+			 - skb_mac_header(skb)) + tcp_hdrlen(skb);
+	struct skb_frag_struct *frag = NULL;
+	int j = 0;
+	uint32_t temp_num_seg = 0;
+
+	/* length of the first chunk of data in the skb minus eit header*/
+	skb_frag_len = skb_headlen(skb) - eit_hdr_len;
+
+	/* Calculate num of segs for skb's first chunk of data*/
+	remainder = skb_frag_len % tso_seg_size;
+	num_segs = skb_frag_len / tso_seg_size;
+	/**
+	 * Remainder non-zero and nr_frags zero implies end of skb data.
+	 * In that case, one more tso seg is required to accommodate
+	 * remaining data, hence num_segs++. If nr_frags is non-zero,
+	 * then remaining data will be accomodated while doing the calculation
+	 * for nr_frags data. Hence, frags_per_tso++.
+	 */
+	if (remainder) {
+		if (!skb_nr_frags)
+			num_segs++;
 		else
-			break;
+			frags_per_tso++;
+	}
+
+	while (skb_nr_frags) {
+		if (j >= skb_shinfo(skb)->nr_frags) {
+			qdf_print("TSO: nr_frags %d j %d\n",
+			skb_shinfo(skb)->nr_frags, j);
+			qdf_assert(0);
+			return 0;
+		}
+		/**
+		 * Calculate the number of tso seg for nr_frags data:
+		 * Get the length of each frag in skb_frag_len, add to
+		 * remainder.Get the number of segments by dividing it to
+		 * tso_seg_size and calculate the new remainder.
+		 * Decrement the nr_frags value and keep
+		 * looping all the skb_fragments.
+		 */
+		frag = &skb_shinfo(skb)->frags[j];
+		skb_frag_len = skb_frag_size(frag);
+		temp_num_seg = num_segs;
+		remainder += skb_frag_len;
+		num_segs += remainder / tso_seg_size;
+		remainder = remainder % tso_seg_size;
+		skb_nr_frags--;
+		if (remainder) {
+			if (num_segs > temp_num_seg)
+				frags_per_tso = 0;
+			/**
+			 * increment the tso per frags whenever remainder is
+			 * positive. If frags_per_tso reaches the (max-1),
+			 * [First frags always have EIT header, therefore max-1]
+			 * increment the num_segs as no more data can be
+			 * accomodated in the curr tso seg. Reset the remainder
+			 * and frags per tso and keep looping.
+			 */
+			frags_per_tso++;
+			if (frags_per_tso == FRAG_NUM_MAX - 1) {
+				num_segs++;
+				frags_per_tso = 0;
+				remainder = 0;
+			}
+			/**
+			 * If this is the last skb frag and still remainder is
+			 * non-zero(frags_per_tso is not reached to the max-1)
+			 * then increment the num_segs to take care of the
+			 * remaining length.
+			 */
+			if (!skb_nr_frags && remainder) {
+				num_segs++;
+				frags_per_tso = 0;
+			}
+		} else {
+			 /* Whenever remainder is 0, reset the frags_per_tso. */
+			frags_per_tso = 0;
+		}
+		j++;
 	}
 
 	return num_segs;