[qca-edma] Add PPPoE support for per-precedence stats

Add support to update per-precedence stats for PPPoE packets

Change-Id: I38e881fb82881da2fd7873874de0cca232e43e0a
Signed-off-by: Rakesh Nair <ranair@codeaurora.org>
diff --git a/edma.c b/edma.c
index 3932454..c67be79 100644
--- a/edma.c
+++ b/edma.c
@@ -1149,6 +1149,97 @@
 	etdr->sw_next_to_fill = start_index;
 }
 
+/* edma_get_v4_precedence()
+ *	Function to retrieve precedence for IPv4
+ */
+static inline int edma_get_v4_precedence(struct sk_buff *skb, int nh_offset, u8 *precedence)
+{
+	const struct iphdr *iph;
+	struct iphdr iph_hdr;
+
+	iph = skb_header_pointer(skb, nh_offset, sizeof(iph_hdr), &iph_hdr);
+
+	if (!iph || iph->ihl < 5)
+		return -1;
+
+	*precedence = iph->tos >> EDMA_DSCP_PREC_SHIFT;
+
+	return 0;
+}
+
+/* edma_get_v6_precedence()
+ *	Function to retrieve precedence for IPv6
+ */
+static inline int edma_get_v6_precedence(struct sk_buff *skb, int nh_offset, u8 *precedence)
+{
+	const struct ipv6hdr *iph;
+	struct ipv6hdr iph_hdr;
+
+	iph = skb_header_pointer(skb, nh_offset, sizeof(iph_hdr), &iph_hdr);
+
+	if (!iph)
+		return -1;
+
+	*precedence = iph->priority >> EDMA_DSCP6_PREC_SHIFT;
+
+	return 0;
+}
+
+/* edma_get_skb_precedence()
+ *	Function to retrieve precedence from skb
+ */
+static int edma_get_skb_precedence(struct sk_buff *skb, u8 *precedence)
+{
+	int nhoff = skb_network_offset(skb);
+	__be16 proto = skb->protocol;
+	int ret;
+	struct pppoeh_proto *pppoeh, ppp_hdr;
+
+	switch(proto) {
+		case __constant_htons(ETH_P_IP): {
+			ret = edma_get_v4_precedence(skb, nhoff, precedence);
+			if (ret)
+				return -1;
+			break;
+		}
+		case __constant_htons(ETH_P_IPV6): {
+			ret = edma_get_v6_precedence(skb, nhoff, precedence);
+			if (ret)
+				return -1;
+			break;
+		}
+		case __constant_htons(ETH_P_PPP_SES): {
+			pppoeh = skb_header_pointer(skb, nhoff, sizeof(ppp_hdr), &ppp_hdr);
+			if (!pppoeh)
+				return -1;
+
+			proto = pppoeh->proto;
+			nhoff += PPPOE_SES_HLEN;
+			switch (proto) {
+				case __constant_htons(PPP_IP): {
+					ret = edma_get_v4_precedence(skb, nhoff, precedence);
+					if (ret)
+						return -1;
+					break;
+				}
+				case __constant_htons(PPP_IPV6): {
+					ret = edma_get_v6_precedence(skb, nhoff, precedence);
+					if (ret)
+						return -1;
+					break;
+				}
+				default:
+					return -1;
+			}
+			break;
+		}
+		default:
+			return -1;
+	}
+
+	return 0;
+}
+
 /* edma_tx_map_and_fill()
  *	gets called from edma_xmit_frame
  *
@@ -1385,32 +1476,15 @@
 
 	/* If sysctl support for per-precedence stats are enabled */
 	if (edma_per_prec_stats_enable) {
-		struct iphdr *ip_hdr = NULL;
-		struct ipv6hdr *ip6_hdr = NULL;
-		uint8_t precedence = 0xff;
+		uint8_t precedence = 0;
 
-		if (likely(htons(ETH_P_IP) == skb->protocol)) {
-			ip_hdr = (struct iphdr *)skb_network_header(skb);
-			if (ip_hdr && ((ip_hdr->protocol == IPPROTO_UDP) || (ip_hdr->protocol == IPPROTO_TCP)))
-				 precedence = ip_hdr->tos >> EDMA_DSCP_PREC_SHIFT;
-
+		if(!edma_get_skb_precedence(skb, &precedence)) {
 			/* Increment per-precedence counters for tx packets
 			 * and set the precedence in the TPD.
 			 */
 			edma_cinfo->edma_ethstats.tx_prec[precedence]++;
 			edma_cinfo->edma_ethstats.tx_ac[edma_dscp2ac_tbl[precedence]]++;
 			tpd->word3 |= precedence << EDMA_TPD_PRIO_SHIFT;
-		} else if (htons(ETH_P_IPV6) == skb->protocol) {
-			ip6_hdr = (struct ipv6hdr *)skb_network_header(skb);
-			if (ip6_hdr && ((ip6_hdr->nexthdr == IPPROTO_UDP) || (ip6_hdr->nexthdr == IPPROTO_TCP)))
-				precedence = ip6_hdr->priority >> EDMA_DSCP6_PREC_SHIFT;
-
-			/* Increment per-precedence counters for tx packets
-			 * and set the precedence in the TPD for v6 packets.
-			 */
-			edma_cinfo->edma_ethstats.tx_prec[precedence]++;
-			edma_cinfo->edma_ethstats.tx_ac[edma_dscp2ac_tbl[precedence]]++;
-			tpd->word3 |= precedence << EDMA_TPD_PRIO_SHIFT;
 		}
 	}