[qca-nss-sfe] SAWF related changes in SFE.

1. Adding sawf_metadata in SFE connection information
2. Addition of per cpu per service class stats DB as part of SFE.

Change-Id: I44e2a0d7b6144ee28a24fc5edd21fdc1cd439142
Signed-off-by: Parikshit Gune <quic_pgune@quicinc.com>
diff --git a/exports/sfe_api.h b/exports/sfe_api.h
index da9c857..a5fcf95 100644
--- a/exports/sfe_api.h
+++ b/exports/sfe_api.h
@@ -23,6 +23,9 @@
 
 #define SFE_MAX_VLAN_DEPTH 2
 #define SFE_VLAN_ID_NOT_CONFIGURED 0xfff
+#define SFE_MAX_SERVICE_CLASS_ID 0x80
+#define SFE_INVALID_SERVICE_CLASS_ID 0xff
+#define SFE_INVALID_MSDUQ 0xff
 
 #define SFE_SPECIAL_INTERFACE_BASE 0x7f00
 #define SFE_SPECIAL_INTERFACE_IPV4 (SFE_SPECIAL_INTERFACE_BASE + 1)
@@ -247,6 +250,15 @@
 };
 
 /**
+ * sfe_service_class_rule
+ *	SFE service class rule information in both direction.
+ */
+struct sfe_service_class_rule {
+	uint32_t flow_mark;		/**< Service class information in flow direction */
+	uint32_t return_mark;		/**< Service class information in return direction */
+};
+
+/**
  * The IPv4 rule create sub-message structure.
  */
 struct sfe_ipv4_rule_create_msg {
@@ -269,6 +281,8 @@
 	struct sfe_acceleration_direction_rule direction_rule;/* Direction related accleration parameters*/
 #endif
 	/* Response */
+	struct sfe_service_class_rule sawf_rule;
+							/**< Service class related information */
 	u32 index;					/**< Slot ID for cache stats to host OS */
 };
 
@@ -392,6 +406,7 @@
 	/*
 	 * Response
 	 */
+	struct sfe_service_class_rule sawf_rule;	/**< Service class related information */
 	u32 index;					/**< Slot ID for cache stats to host OS */
 };
 
diff --git a/sfe.h b/sfe.h
index 6cadbfe..e1fe4ec 100644
--- a/sfe.h
+++ b/sfe.h
@@ -34,6 +34,23 @@
 
 #define SFE_L2_PARSE_FLAGS_PPPOE_INGRESS 0x01	/* Indicates presence of a valid PPPoE header */
 
+/**
+ * SAWF_metadata information placement in mark field.
+ */
+#define SFE_SAWF_VALID_TAG 0xAA
+#define SFE_SAWF_TAG_SHIFT 0x18
+#define SFE_SAWF_SERVICE_CLASS_SHIFT 0x10
+#define SFE_SAWF_SERVICE_CLASS_MASK 0xff
+#define SFE_SAWF_MSDUQ_MASK 0xffff
+
+/**
+ * SAWF_metadata extraction.
+ */
+#define SFE_GET_SAWF_TAG(x) (x>>SFE_SAWF_TAG_SHIFT)
+#define SFE_GET_SAWF_SERVICE_CLASS(x) ((x>>SFE_SAWF_SERVICE_CLASS_SHIFT) & SFE_SAWF_SERVICE_CLASS_MASK)
+#define SFE_GET_SAWF_MSDUQ(x) (x & SFE_SAWF_MSDUQ_MASK)
+#define SFE_SAWF_TAG_IS_VALID(x) ((x == SFE_SAWF_VALID_TAG) ? true : false)
+
 /*
  * IPv6 address structure
  */
diff --git a/sfe_ipv4.c b/sfe_ipv4.c
index 3b3d1cb..b5416d0 100644
--- a/sfe_ipv4.c
+++ b/sfe_ipv4.c
@@ -32,6 +32,7 @@
 #include <linux/netfilter.h>
 #include <linux/inetdevice.h>
 #include <linux/netfilter_ipv4.h>
+#include <linux/seqlock.h>
 #include <net/protocol.h>
 #include <net/gre.h>
 
@@ -733,6 +734,21 @@
 }
 
 /*
+ * sfe_ipv4_service_class_stats_inc()
+ *	Increment per cpu per service class stats.
+ */
+void sfe_ipv4_service_class_stats_inc(struct sfe_ipv4 *si, uint8_t sid, uint64_t bytes)
+{
+	struct sfe_ipv4_service_class_stats_db *sc_stats_db = this_cpu_ptr(si->stats_pcpu_psc);
+	struct sfe_ipv4_per_service_class_stats *sc_stats = &sc_stats_db->psc_stats[sid];
+
+	write_seqcount_begin(&sc_stats->seq);
+	sc_stats->tx_bytes += bytes;
+	sc_stats->tx_packets++;
+	write_seqcount_end(&sc_stats->seq);
+}
+
+/*
  * sfe_ipv4_exception_stats_inc()
  *	Increment exception stats.
  */
@@ -1055,6 +1071,8 @@
 	struct net *net;
 	struct sock *sk;
 	unsigned int src_if_idx;
+	u32 flow_sawf_tag;
+	u32 return_sawf_tag;
 
 	if (msg->rule_flags & SFE_RULE_CREATE_FLAG_USE_FLOW_BOTTOM_INTERFACE) {
 		flow_interface_num = msg->conn_rule.flow_interface_num;
@@ -1232,6 +1250,17 @@
 	}
 
 	/*
+	 * Mark SAWF metadata if the sawf tag is valid and set.
+	 */
+	original_cm->sawf_valid = false;
+	flow_sawf_tag = SFE_GET_SAWF_TAG(msg->sawf_rule.flow_mark);
+	if (likely(SFE_SAWF_TAG_IS_VALID(flow_sawf_tag))) {
+		original_cm->mark = msg->sawf_rule.flow_mark;
+		original_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_MARK;
+		original_cm->sawf_valid = true;
+	}
+
+	/*
 	 * Add VLAN rule to original_cm
 	 */
 	if (msg->valid_flags & SFE_RULE_CREATE_VLAN_VALID) {
@@ -1435,6 +1464,17 @@
 	}
 
 	/*
+	 * Mark SAWF metadata in reply match if the sawf tag is valid.
+	 */
+	reply_cm->sawf_valid = false;
+	return_sawf_tag = SFE_GET_SAWF_TAG(msg->sawf_rule.return_mark);
+	if (likely(SFE_SAWF_TAG_IS_VALID(return_sawf_tag))) {
+		reply_cm->mark = msg->sawf_rule.return_mark;
+		reply_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_MARK;
+		reply_cm->sawf_valid = true;
+	}
+
+	/*
 	 * Setup UDP Socket if found to be valid for decap.
 	 */
 	RCU_INIT_POINTER(reply_cm->up, NULL);
@@ -1991,6 +2031,9 @@
 	u64 dest_rx_bytes;
 	u64 last_sync_jiffies;
 	u32 src_mark, dest_mark, src_priority, dest_priority, src_dscp, dest_dscp;
+	bool original_cm_sawf_valid, reply_cm_sawf_valid;
+	u32 flow_service_class, return_service_class;
+	u32 flow_msduq, return_msduq;
 	u32 packet, byte, original_cm_flags;
 	u16 pppoe_session_id;
 	u8 pppoe_remote_mac[ETH_ALEN];
@@ -2051,7 +2094,12 @@
 	original_cm_flags = original_cm->flags;
 	pppoe_session_id = original_cm->pppoe_session_id;
 	ether_addr_copy(pppoe_remote_mac, original_cm->pppoe_remote_mac);
-
+	original_cm_sawf_valid = original_cm->sawf_valid;
+	reply_cm_sawf_valid = reply_cm->sawf_valid;
+	flow_service_class = SFE_GET_SAWF_SERVICE_CLASS(original_cm->mark);
+	flow_msduq = SFE_GET_SAWF_MSDUQ(original_cm->mark);
+	return_service_class = SFE_GET_SAWF_SERVICE_CLASS(reply_cm->mark);
+	return_msduq = SFE_GET_SAWF_MSDUQ(reply_cm->mark);
 #ifdef CONFIG_NF_FLOW_COOKIE
 	src_flow_cookie = original_cm->flow_cookie;
 	dst_flow_cookie = reply_cm->flow_cookie;
@@ -2103,6 +2151,16 @@
 				pppoe_session_id, pppoe_remote_mac);
 	}
 
+	if (original_cm_sawf_valid) {
+		bytes_read += snprintf(msg + bytes_read, CHAR_DEV_MSG_SIZE, "flow_service_class=\"%d\" flow_msduq=\"%d\" ",
+				flow_service_class, flow_msduq);
+	}
+
+	if (reply_cm_sawf_valid) {
+		bytes_read += snprintf(msg + bytes_read, CHAR_DEV_MSG_SIZE, "return_service_class=\"%d\" return_msduq=\"%d\" ",
+				return_service_class, return_msduq);
+	}
+
 	bytes_read += snprintf(msg + bytes_read, CHAR_DEV_MSG_SIZE, "/>\n");
 
 	if (copy_to_user(buffer + *total_read, msg, CHAR_DEV_MSG_SIZE)) {
@@ -2553,12 +2611,22 @@
 	}
 
 	/*
+	 * Allocate per cpu per service class memory.
+	 */
+	si->stats_pcpu_psc = alloc_percpu_gfp(struct sfe_ipv4_service_class_stats_db,
+						GFP_KERNEL | __GFP_ZERO);
+	if (!si->stats_pcpu_psc) {
+		DEBUG_ERROR("failed to allocate per cpu per service clas stats memory\n");
+		goto exit1;
+	}
+
+	/*
 	 * Create sys/sfe_ipv4
 	 */
 	si->sys_ipv4 = kobject_create_and_add("sfe_ipv4", NULL);
 	if (!si->sys_ipv4) {
 		DEBUG_ERROR("failed to register sfe_ipv4\n");
-		goto exit1;
+		goto exit2;
 	}
 
 	/*
@@ -2567,20 +2635,20 @@
 	result = sysfs_create_file(si->sys_ipv4, &sfe_ipv4_debug_dev_attr.attr);
 	if (result) {
 		DEBUG_ERROR("failed to register debug dev file: %d\n", result);
-		goto exit2;
+		goto exit3;
 	}
 
 	result = sysfs_create_file(si->sys_ipv4, &sfe_ipv4_cpu_attr.attr);
 	if (result) {
 		DEBUG_ERROR("failed to register debug dev file: %d\n", result);
-		goto exit3;
+		goto exit4;
 	}
 
 #ifdef CONFIG_NF_FLOW_COOKIE
 	result = sysfs_create_file(si->sys_ipv4, &sfe_ipv4_flow_cookie_attr.attr);
 	if (result) {
 		DEBUG_ERROR("failed to register flow cookie enable file: %d\n", result);
-		goto exit4;
+		goto exit5;
 	}
 #endif /* CONFIG_NF_FLOW_COOKIE */
 
@@ -2592,7 +2660,7 @@
 #endif
 	if (result < 0) {
 		DEBUG_ERROR("can't register nf local out hook: %d\n", result);
-		goto exit5;
+		goto exit6;
 	}
 	DEBUG_INFO("Register nf local out hook success: %d\n", result);
 #endif
@@ -2602,7 +2670,7 @@
 	result = register_chrdev(0, "sfe_ipv4", &sfe_ipv4_debug_dev_fops);
 	if (result < 0) {
 		DEBUG_ERROR("Failed to register chrdev: %d\n", result);
-		goto exit6;
+		goto exit7;
 	}
 
 	si->debug_dev = result;
@@ -2617,7 +2685,7 @@
 	spin_lock_init(&si->lock);
 	return 0;
 
-exit6:
+exit7:
 #ifdef SFE_PROCESS_LOCAL_OUT
 	DEBUG_TRACE("sfe: Unregister local out hook\n");
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0))
@@ -2625,20 +2693,23 @@
 #else
 	nf_unregister_net_hooks(&init_net, sfe_ipv4_ops_local_out, ARRAY_SIZE(sfe_ipv4_ops_local_out));
 #endif
-exit5:
+exit6:
 #endif
 #ifdef CONFIG_NF_FLOW_COOKIE
 	sysfs_remove_file(si->sys_ipv4, &sfe_ipv4_flow_cookie_attr.attr);
 
-exit4:
+exit5:
 #endif /* CONFIG_NF_FLOW_COOKIE */
 	sysfs_remove_file(si->sys_ipv4, &sfe_ipv4_cpu_attr.attr);
-exit3:
+exit4:
 	sysfs_remove_file(si->sys_ipv4, &sfe_ipv4_debug_dev_attr.attr);
 
-exit2:
+exit3:
 	kobject_put(si->sys_ipv4);
 
+exit2:
+	free_percpu(si->stats_pcpu_psc);
+
 exit1:
 	free_percpu(si->stats_pcpu);
 
@@ -2682,6 +2753,7 @@
 	kobject_put(si->sys_ipv4);
 
 	free_percpu(si->stats_pcpu);
+	free_percpu(si->stats_pcpu_psc);
 }
 
 #ifdef CONFIG_NF_FLOW_COOKIE
diff --git a/sfe_ipv4.h b/sfe_ipv4.h
index 865a93d..428af90 100644
--- a/sfe_ipv4.h
+++ b/sfe_ipv4.h
@@ -194,6 +194,7 @@
 	 * xmit device's feature
 	 */
 	netdev_features_t features;
+	bool sawf_valid;		/* Indicates mark has valid SAWF information */
 };
 
 /*
@@ -323,6 +324,28 @@
 };
 
 /*
+ * sfe_ipv4_per_service_class_stats
+ *	Per service class stats
+ */
+struct sfe_ipv4_per_service_class_stats {
+	u64 tx_bytes;		/* Byte count */
+	u64 tx_packets;		/* Packet count */
+	seqcount_t seq;		/* seq lock for read/write protection */
+	/*
+	 * TODO : add entries to be collected later.
+	 */
+};
+
+/*
+ * sfe_ipv4_service_class_stats_db
+ *	stat entries for each service class.
+ */
+struct sfe_ipv4_service_class_stats_db {
+	struct sfe_ipv4_per_service_class_stats psc_stats[SFE_MAX_SERVICE_CLASS_ID];
+				/*  Per service class stats */
+};
+
+/*
  * Per-module structure.
  */
 struct sfe_ipv4 {
@@ -351,6 +374,8 @@
 	int flow_cookie_enable;
 					/* Enable/disable flow cookie at runtime */
 #endif
+	struct sfe_ipv4_service_class_stats_db __percpu *stats_pcpu_psc;
+					/* Database to maintain per cpu per service class statistics */
 
 	struct sfe_ipv4_stats __percpu *stats_pcpu;
 					/* Per CPU statistics. */
@@ -394,6 +419,7 @@
 						  int *total_read, struct sfe_ipv4_debug_xml_write_state *ws);
 
 u16 sfe_ipv4_gen_ip_csum(struct iphdr *iph);
+void sfe_ipv4_service_class_stats_inc(struct sfe_ipv4 *si, uint8_t sid, uint64_t bytes);
 void sfe_ipv4_exception_stats_inc(struct sfe_ipv4 *si, enum sfe_ipv4_exception_events reason);
 bool sfe_ipv4_remove_connection(struct sfe_ipv4 *si, struct sfe_ipv4_connection *c);
 void sfe_ipv4_flush_connection(struct sfe_ipv4 *si, struct sfe_ipv4_connection *c, sfe_sync_reason_t reason);
diff --git a/sfe_ipv4_tcp.c b/sfe_ipv4_tcp.c
index 14283ea..fdb73e3 100644
--- a/sfe_ipv4_tcp.c
+++ b/sfe_ipv4_tcp.c
@@ -127,6 +127,7 @@
 	struct sfe_ipv4_connection_match *counter_cm;
 	u8 ttl;
 	u32 flags;
+	u32 service_class_id;
 	struct net_device *xmit_dev;
 	bool ret;
 	bool hw_csum;
@@ -695,6 +696,13 @@
 	 */
 	if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_MARK)) {
 		skb->mark = cm->mark;
+		/*
+		 * Update service class stats if SAWF is valid.
+		 */
+		if (likely(cm->sawf_valid)) {
+			service_class_id = SFE_GET_SAWF_SERVICE_CLASS(cm->mark);
+			sfe_ipv4_service_class_stats_inc(si, service_class_id, len);
+		}
 	}
 
 	/*
diff --git a/sfe_ipv4_udp.c b/sfe_ipv4_udp.c
index 61ed28e..abaa873 100644
--- a/sfe_ipv4_udp.c
+++ b/sfe_ipv4_udp.c
@@ -127,6 +127,7 @@
 	__be16 dest_port;
 	struct sfe_ipv4_connection_match *cm;
 	u8 ttl;
+	u32 service_class_id;
 	struct net_device *xmit_dev;
 	bool hw_csum;
 	int err;
@@ -538,6 +539,13 @@
 	 */
 	if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_MARK)) {
 		skb->mark = cm->mark;
+		/*
+		 * Update service class stats if SAWF is valid.
+		 */
+		if (likely(cm->sawf_valid)) {
+			service_class_id = SFE_GET_SAWF_SERVICE_CLASS(cm->mark);
+			sfe_ipv4_service_class_stats_inc(si, service_class_id, len);
+		}
 	}
 
 	/*
diff --git a/sfe_ipv6.c b/sfe_ipv6.c
index 162a1d7..0549bf4 100644
--- a/sfe_ipv6.c
+++ b/sfe_ipv6.c
@@ -31,6 +31,7 @@
 #include <linux/netfilter.h>
 #include <linux/inetdevice.h>
 #include <linux/netfilter_ipv6.h>
+#include <linux/seqlock.h>
 #include <net/protocol.h>
 #include <net/addrconf.h>
 #include <net/gre.h>
@@ -750,7 +751,22 @@
 	call_rcu(&c->rcu, sfe_ipv6_free_sfe_ipv6_connection_rcu);
 }
 
- /*
+/*
+ * sfe_ipv6_service_class_stats_inc()
+ *	Increment per cpu per service class stats.
+ */
+void sfe_ipv6_service_class_stats_inc(struct sfe_ipv6 *si, uint8_t sid, uint64_t bytes)
+{
+	struct sfe_ipv6_service_class_stats_db *sc_stats_db = this_cpu_ptr(si->stats_pcpu_psc);
+	struct sfe_ipv6_per_service_class_stats *sc_stats = &sc_stats_db->psc_stats[sid];
+
+	write_seqcount_begin(&sc_stats->seq);
+	sc_stats->tx_bytes += bytes;
+	sc_stats->tx_packets++;
+	write_seqcount_end(&sc_stats->seq);
+}
+
+/*
  * sfe_ipv6_exception_stats_inc()
  *	Increment exception stats.
  */
@@ -1063,6 +1079,8 @@
 
 	s32 flow_interface_num = msg->conn_rule.flow_top_interface_num;
 	s32 return_interface_num = msg->conn_rule.return_top_interface_num;
+	u32 flow_sawf_tag;
+	u32 return_sawf_tag;
 
 	if (msg->rule_flags & SFE_RULE_CREATE_FLAG_USE_FLOW_BOTTOM_INTERFACE) {
 		flow_interface_num = msg->conn_rule.flow_interface_num;
@@ -1228,6 +1246,16 @@
 		original_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_FAST_XMIT_DEV_ADMISSION;
 	}
 
+	/*
+	 * Mark SAWF metadata if the sawf tag is valid.
+	 */
+	original_cm->sawf_valid = false;
+	flow_sawf_tag = SFE_GET_SAWF_TAG(msg->sawf_rule.flow_mark);
+	if (likely(SFE_SAWF_TAG_IS_VALID(flow_sawf_tag))) {
+		original_cm->mark = msg->sawf_rule.flow_mark;
+		original_cm->sawf_valid = true;
+		original_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_MARK;
+	}
 
 	/*
 	 * Add VLAN rule to original_cm
@@ -1413,6 +1441,17 @@
 	}
 
 	/*
+	 * Mark return SAWF metadata if the sawf tag is valid.
+	 */
+	reply_cm->sawf_valid = false;
+	return_sawf_tag = SFE_GET_SAWF_TAG(msg->sawf_rule.return_mark);
+	if (likely(SFE_SAWF_TAG_IS_VALID(return_sawf_tag))) {
+		reply_cm->mark = msg->sawf_rule.return_mark;
+		reply_cm->sawf_valid = true;
+		reply_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_MARK;
+	}
+
+	/*
 	 * Setup UDP Socket if found to be valid for decap.
 	 */
 	RCU_INIT_POINTER(reply_cm->up, NULL);
@@ -1966,6 +2005,9 @@
 	u64 dest_rx_bytes;
 	u64 last_sync_jiffies;
 	u32 src_mark, dest_mark,  src_priority, dest_priority, src_dscp, dest_dscp;
+	bool original_cm_sawf_valid, reply_cm_sawf_valid;
+	u32 flow_service_class, return_service_class;
+	u32 flow_msduq, return_msduq;
 	u32 packet, byte, original_cm_flags;
 	u16 pppoe_session_id;
 	u8 pppoe_remote_mac[ETH_ALEN];
@@ -2026,6 +2068,13 @@
 	ether_addr_copy(pppoe_remote_mac, original_cm->pppoe_remote_mac);
 	dest_mark = reply_cm->mark;
 	reply_fast_xmit = reply_cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_FAST_XMIT;
+	original_cm_sawf_valid = original_cm->sawf_valid;
+	reply_cm_sawf_valid = reply_cm->sawf_valid;
+	flow_service_class = SFE_GET_SAWF_SERVICE_CLASS(original_cm->mark);
+	flow_msduq = SFE_GET_SAWF_MSDUQ(original_cm->mark);
+	return_service_class = SFE_GET_SAWF_SERVICE_CLASS(reply_cm->mark);
+	return_msduq = SFE_GET_SAWF_MSDUQ(reply_cm->mark);
+
 #ifdef CONFIG_NF_FLOW_COOKIE
 	src_flow_cookie = original_cm->flow_cookie;
 	dst_flow_cookie = reply_cm->flow_cookie;
@@ -2077,6 +2126,16 @@
 			pppoe_session_id, pppoe_remote_mac);
 	}
 
+	if (original_cm_sawf_valid) {
+		bytes_read += snprintf(msg + bytes_read, CHAR_DEV_MSG_SIZE, "flow_service_class=\"%d\" flow_msduq=\"%d\" ",
+			flow_service_class, flow_msduq);
+	}
+
+	if (reply_cm_sawf_valid) {
+		bytes_read += snprintf(msg + bytes_read, CHAR_DEV_MSG_SIZE, "return_service_class=\"%d\" return_msduq=\"%d\" ",
+			return_service_class, return_msduq);
+	}
+
 	bytes_read += snprintf(msg + bytes_read, CHAR_DEV_MSG_SIZE, ")/>\n");
 
 	if (copy_to_user(buffer + *total_read, msg, CHAR_DEV_MSG_SIZE)) {
@@ -2533,12 +2592,22 @@
 	}
 
 	/*
+	 * Allocate per cpu per service class memory.
+	 */
+	si->stats_pcpu_psc = alloc_percpu_gfp(struct sfe_ipv6_service_class_stats_db,
+						GFP_KERNEL | __GFP_ZERO);
+	if (!si->stats_pcpu_psc) {
+		DEBUG_ERROR("failed to allocate per cpu per service clas stats memory\n");
+		goto exit1;
+	}
+
+	/*
 	 * Create sys/sfe_ipv6
 	 */
 	si->sys_ipv6 = kobject_create_and_add("sfe_ipv6", NULL);
 	if (!si->sys_ipv6) {
 		DEBUG_ERROR("failed to register sfe_ipv6\n");
-		goto exit1;
+		goto exit2;
 	}
 
 	/*
@@ -2547,20 +2616,20 @@
 	result = sysfs_create_file(si->sys_ipv6, &sfe_ipv6_debug_dev_attr.attr);
 	if (result) {
 		DEBUG_ERROR("failed to register debug dev file: %d\n", result);
-		goto exit2;
+		goto exit3;
 	}
 
 	result = sysfs_create_file(si->sys_ipv6, &sfe_ipv6_cpu_attr.attr);
 	if (result) {
 		DEBUG_ERROR("failed to register debug dev file: %d\n", result);
-		goto exit3;
+		goto exit4;
 	}
 
 #ifdef CONFIG_NF_FLOW_COOKIE
 	result = sysfs_create_file(si->sys_ipv6, &sfe_ipv6_flow_cookie_attr.attr);
 	if (result) {
 		DEBUG_ERROR("failed to register flow cookie enable file: %d\n", result);
-		goto exit4;
+		goto exit5;
 	}
 #endif /* CONFIG_NF_FLOW_COOKIE */
 
@@ -2573,7 +2642,7 @@
 #endif
 	if (result < 0) {
 		DEBUG_ERROR("can't register nf local out hook: %d\n", result);
-		goto exit5;
+		goto exit6;
 	} else {
 		DEBUG_ERROR("Register nf local out hook success: %d\n", result);
 	}
@@ -2584,7 +2653,7 @@
 	result = register_chrdev(0, "sfe_ipv6", &sfe_ipv6_debug_dev_fops);
 	if (result < 0) {
 		DEBUG_ERROR("Failed to register chrdev: %d\n", result);
-		goto exit6;
+		goto exit7;
 	}
 
 	si->debug_dev = result;
@@ -2599,7 +2668,7 @@
 
 	return 0;
 
-exit6:
+exit7:
 #ifdef SFE_PROCESS_LOCAL_OUT
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0))
 	DEBUG_TRACE("sfe: Unregister local out hook\n");
@@ -2610,20 +2679,23 @@
 #endif
 #endif
 
-exit5:
+exit6:
 #ifdef CONFIG_NF_FLOW_COOKIE
 	sysfs_remove_file(si->sys_ipv6, &sfe_ipv6_flow_cookie_attr.attr);
 
-exit4:
+exit5:
 #endif /* CONFIG_NF_FLOW_COOKIE */
 	sysfs_remove_file(si->sys_ipv6, &sfe_ipv6_cpu_attr.attr);
 
-exit3:
+exit4:
 	sysfs_remove_file(si->sys_ipv6, &sfe_ipv6_debug_dev_attr.attr);
 
-exit2:
+exit3:
 	kobject_put(si->sys_ipv6);
 
+exit2:
+	free_percpu(si->stats_pcpu_psc);
+
 exit1:
 	free_percpu(si->stats_pcpu);
 
@@ -2650,6 +2722,7 @@
 	unregister_chrdev(si->debug_dev, "sfe_ipv6");
 
 	free_percpu(si->stats_pcpu);
+	free_percpu(si->stats_pcpu_psc);
 
 #ifdef SFE_PROCESS_LOCAL_OUT
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0))
diff --git a/sfe_ipv6.h b/sfe_ipv6.h
index e478d5b..6022300 100644
--- a/sfe_ipv6.h
+++ b/sfe_ipv6.h
@@ -204,9 +204,11 @@
 	u16 l2_hdr_size;
 
 	/*
-	* xmit device's feature
+	 * xmit device's feature
 	 */
 	netdev_features_t features;
+
+	bool sawf_valid;		/* Indicates mark has valid SAWF information. */
 };
 
 /*
@@ -343,6 +345,28 @@
 };
 
 /*
+ * sfe_ipv6_per_service_class_stats
+ *      Per service class stats
+ */
+struct sfe_ipv6_per_service_class_stats {
+	u64 tx_bytes;		/* Byte count */
+	u64 tx_packets;		/* Packet count */
+	seqcount_t seq;		/* seq lock for read/write protection */
+	/*
+	 * TODO : Add the entries to be maintained later.
+	 */
+};
+
+/*
+ * sfe_ipv6_service_class_stats_db
+ *      Stat entries for each service class.
+ */
+struct sfe_ipv6_service_class_stats_db{
+	struct sfe_ipv6_per_service_class_stats psc_stats[SFE_MAX_SERVICE_CLASS_ID];
+					/* Per service class stats */
+};
+
+/*
  * Per-module structure.
  */
 struct sfe_ipv6 {
@@ -368,6 +392,8 @@
 	int flow_cookie_enable;
 					/* Enable/disable flow cookie at runtime */
 #endif
+	struct sfe_ipv6_service_class_stats_db __percpu *stats_pcpu_psc;
+					/* Database to maintain per cpu per service class statistics */
 
 	struct sfe_ipv6_stats __percpu *stats_pcpu;
 					/* Common SFE counters. */
@@ -437,7 +463,7 @@
 }
 
 void sfe_ipv6_exception_stats_inc(struct sfe_ipv6 *si, enum sfe_ipv6_exception_events reason);
-
+void sfe_ipv6_service_class_stats_inc(struct sfe_ipv6 *si, uint8_t sid, uint64_t bytes);
 struct sfe_ipv6_connection_match *
 sfe_ipv6_find_connection_match_rcu(struct sfe_ipv6 *si, struct net_device *dev, u8 protocol,
 					struct sfe_ipv6_addr *src_ip, __be16 src_port,
diff --git a/sfe_ipv6_tcp.c b/sfe_ipv6_tcp.c
index 1f377a2..6f740d8 100644
--- a/sfe_ipv6_tcp.c
+++ b/sfe_ipv6_tcp.c
@@ -126,6 +126,7 @@
 	struct sfe_ipv6_connection_match *cm;
 	struct sfe_ipv6_connection_match *counter_cm;
 	u32 flags;
+	u32 service_class_id;
 	struct net_device *xmit_dev;
 	bool ret;
 	bool hw_csum;
@@ -697,6 +698,13 @@
 	 */
 	if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_MARK)) {
 		skb->mark = cm->mark;
+		/*
+		 * Update service class stats if SAWF is valid.
+		 */
+		if (likely(cm->sawf_valid)) {
+			service_class_id = SFE_GET_SAWF_SERVICE_CLASS(cm->mark);
+			sfe_ipv6_service_class_stats_inc(si, service_class_id, len);
+		}
 	}
 
 	/*
diff --git a/sfe_ipv6_udp.c b/sfe_ipv6_udp.c
index 3df6d51..08263bd 100644
--- a/sfe_ipv6_udp.c
+++ b/sfe_ipv6_udp.c
@@ -132,6 +132,7 @@
 	struct sfe_ipv6_addr *dest_ip;
 	__be16 src_port;
 	__be16 dest_port;
+	u32 service_class_id;
 	struct sfe_ipv6_connection_match *cm;
 	struct net_device *xmit_dev;
 	int ret;
@@ -529,6 +530,13 @@
 	 */
 	if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_MARK)) {
 		skb->mark = cm->mark;
+		/*
+		 * Update service class stats if SAWF is valid.
+		 */
+		if (likely(cm->sawf_valid)) {
+			service_class_id = SFE_GET_SAWF_SERVICE_CLASS(cm->mark);
+			sfe_ipv6_service_class_stats_inc(si, service_class_id, len);
+		}
 	}
 
 	/*