Merge "[qca-nss-ecm] Request connections stats from ECM"
diff --git a/frontends/nss/ecm_nss_ipv4.c b/frontends/nss/ecm_nss_ipv4.c
index 3a0a6fb..578a146 100644
--- a/frontends/nss/ecm_nss_ipv4.c
+++ b/frontends/nss/ecm_nss_ipv4.c
@@ -105,6 +105,9 @@
 
 #include "ecm_front_end_common.h"
 
+#define ECM_NSS_IPV4_STATS_SYNC_PERIOD msecs_to_jiffies(1000)
+#define ECM_NSS_IPV4_STATS_SYNC_UDELAY 4000	/* Delay for 4 ms */
+
 int ecm_nss_ipv4_no_action_limit_default = 250;		/* Default no-action limit. */
 int ecm_nss_ipv4_driver_fail_limit_default = 250;		/* Default driver fail limit. */
 int ecm_nss_ipv4_nack_limit_default = 250;			/* Default nack limit. */
@@ -155,6 +158,17 @@
 static int ecm_nss_ipv4_stopped = 0;			/* When non-zero further traffic will not be processed */
 
 /*
+ * Workqueue for the connection sync
+ */
+struct workqueue_struct *ecm_nss_ipv4_workqueue;
+struct delayed_work ecm_nss_ipv4_work;
+struct nss_ipv4_msg *ecm_nss_ipv4_sync_req_msg;
+static unsigned long int ecm_nss_ipv4_next_req_time;
+static unsigned long int ecm_nss_ipv4_stats_request_success = 0;	/* Number of success stats request */
+static unsigned long int ecm_nss_ipv4_stats_request_fail = 0;		/* Number of failed stats request */
+static unsigned long int ecm_nss_ipv4_stats_request_nack = 0;		/* Number of NACK'd stats request */
+
+/*
  * ecm_nss_ipv4_node_establish_and_ref()
  *	Returns a reference to a node, possibly creating one if necessary.
  *
@@ -1550,12 +1564,11 @@
 }
 
 /*
- * ecm_nss_ipv4_net_dev_callback()
- *	Callback handler from the NSS.
+ * ecm_nss_ipv4_process_one_conn_sync_msg()
+ *	Process one connection sync message.
  */
-static void ecm_nss_ipv4_net_dev_callback(void *app_data, struct nss_ipv4_msg *nim)
+static inline void ecm_nss_ipv4_process_one_conn_sync_msg(struct nss_ipv4_conn_sync *sync)
 {
-	struct nss_ipv4_conn_sync *sync;
 	struct nf_conntrack_tuple_hash *h;
 	struct nf_conntrack_tuple tuple;
 	struct nf_conn *ct;
@@ -1572,15 +1585,6 @@
 	struct ecm_classifier_rule_sync class_sync;
 
 	/*
-	 * Only respond to sync messages
-	 */
-	if (nim->cm.type != NSS_IPV4_RX_CONN_STATS_SYNC_MSG) {
-		DEBUG_TRACE("Ignoring nim: %p - not sync: %d", nim, nim->cm.type);
-		return;
-	}
-	sync = &nim->msg.conn_stats;
-
-	/*
 	 * Look up ecm connection with a view to synchronising the connection, classifier and data tracker.
 	 * Note that we use _xlate versions for destination - for egressing connections this would be the wan IP address,
 	 * but for ingressing this would be the LAN side (non-nat'ed) address and is what we need for lookup of our connection.
@@ -1902,6 +1906,110 @@
 }
 
 /*
+ * ecm_nss_ipv4_net_dev_callback()
+ *	Callback handler from the NSS.
+ */
+static void ecm_nss_ipv4_net_dev_callback(void *app_data, struct nss_ipv4_msg *nim)
+{
+	struct nss_ipv4_conn_sync *sync;
+
+	/*
+	 * Only respond to sync messages
+	 */
+	if (nim->cm.type != NSS_IPV4_RX_CONN_STATS_SYNC_MSG) {
+		DEBUG_TRACE("Ignoring nim: %p - not sync: %d", nim, nim->cm.type);
+		return;
+	}
+	sync = &nim->msg.conn_stats;
+	ecm_nss_ipv4_process_one_conn_sync_msg(sync);
+}
+
+/*
+ * ecm_nss_ipv4_connection_sync_many_callback()
+ *	Callback for conn_sync_many request message
+ */
+static void ecm_nss_ipv4_connection_sync_many_callback(void *app_data, struct nss_ipv4_msg *nim)
+{
+	struct nss_ipv4_conn_sync_many_msg *nicsm = &nim->msg.conn_stats_many;
+	int i;
+
+	/*
+	 * If ECM is terminating, don't process this final stats
+	 */
+	if (ecm_nss_ipv4_terminate_pending) {
+		return;
+	}
+
+	if (nim->cm.response == NSS_CMN_RESPONSE_ACK) {
+		for (i = 0; i < nicsm->count; i++) {
+			ecm_nss_ipv4_process_one_conn_sync_msg(&nicsm->conn_sync[i]);
+		}
+		ecm_nss_ipv4_sync_req_msg->msg.conn_stats_many.index = nicsm->next;
+	} else {
+		/*
+		 * We get a NACK from FW, which should not happen, restart the request
+		 */
+		DEBUG_WARN("IPv4 conn stats request failed, restarting\n");
+		ecm_nss_ipv4_stats_request_nack++;
+		ecm_nss_ipv4_sync_req_msg->msg.conn_stats_many.index = 0;
+	}
+	queue_delayed_work(ecm_nss_ipv4_workqueue, &ecm_nss_ipv4_work, 0);
+}
+
+/*
+ * ecm_nss_ipv4_stats_sync_req_work()
+ *      Schedule delayed work to process connection stats and request next sync
+ */
+static void ecm_nss_ipv4_stats_sync_req_work(struct work_struct *work)
+{
+	/*
+	 * Prepare a nss_ipv4_msg with CONN_STATS_SYNC_MANY request
+	 */
+	struct nss_ipv4_conn_sync_many_msg *nicsm_req;
+	nss_tx_status_t nss_tx_status;
+	int retry = 3;
+	unsigned long int current_jiffies;
+
+	usleep_range(ECM_NSS_IPV4_STATS_SYNC_UDELAY - 100, ECM_NSS_IPV4_STATS_SYNC_UDELAY);
+
+	nicsm_req = &ecm_nss_ipv4_sync_req_msg->msg.conn_stats_many;
+
+	/*
+	 * If index is 0, we are starting a new round, but if we still have time remain
+	 * in this round, sleep until it ends
+	 */
+	if (nicsm_req->index == 0) {
+		current_jiffies = jiffies;
+		if (ecm_nss_ipv4_next_req_time > current_jiffies) {
+			msleep(jiffies_to_msecs(ecm_nss_ipv4_next_req_time - current_jiffies));
+		}
+		ecm_nss_ipv4_next_req_time = jiffies + ECM_NSS_IPV4_STATS_SYNC_PERIOD;
+	}
+
+	while (retry) {
+		if (ecm_nss_ipv4_terminate_pending) {
+			return;
+		}
+		nss_tx_status = nss_ipv4_tx_with_size(ecm_nss_ipv4_nss_ipv4_mgr, ecm_nss_ipv4_sync_req_msg, PAGE_SIZE);
+		if (nss_tx_status == NSS_TX_SUCCESS) {
+			ecm_nss_ipv4_stats_request_success++;
+			return;
+		}
+		ecm_nss_ipv4_stats_request_fail++;
+		retry--;
+		DEBUG_TRACE("TX_NOT_OKAY, try again later\n");
+		usleep_range(100, 200);
+	}
+
+	/*
+	 * TX failed after retries, reschedule ourselves with fresh start
+	 */
+	nicsm_req->count = 0;
+	nicsm_req->index = 0;
+	queue_delayed_work(ecm_nss_ipv4_workqueue, &ecm_nss_ipv4_work, ECM_NSS_IPV4_STATS_SYNC_PERIOD);
+}
+
+/*
  * struct nf_hook_ops ecm_nss_ipv4_netfilter_hooks[]
  *	Hooks into netfilter packet monitoring points.
  */
@@ -2249,6 +2357,82 @@
 };
 
 /*
+ * ecm_nss_ipv4_get_stats_request_counter()
+ */
+static ssize_t ecm_nss_ipv4_get_stats_request_counter(struct file *file,
+								char __user *user_buf,
+								size_t sz, loff_t *ppos)
+{
+	char *buf;
+	int ret;
+
+	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!buf) {
+		return -ENOMEM;
+	}
+
+	ret = snprintf(buf, (ssize_t)PAGE_SIZE, "success=%lu\tfail=%lu\tnack=%lu\t\n",
+			ecm_nss_ipv4_stats_request_success, ecm_nss_ipv4_stats_request_fail,
+			ecm_nss_ipv4_stats_request_nack);
+	if (ret < 0) {
+		kfree(buf);
+		return -EFAULT;
+	}
+
+	ret = simple_read_from_buffer(user_buf, sz, ppos, buf, ret);
+	kfree(buf);
+	return ret;
+}
+
+/*
+ * File operations for decel command average time.
+ */
+static struct file_operations ecm_nss_ipv4_stats_request_counter_fops = {
+	.read = ecm_nss_ipv4_get_stats_request_counter,
+};
+
+/*
+ * ecm_nss_ipv4_sync_queue_init
+ *	Initialize the workqueue for ipv4 stats sync
+ */
+static bool ecm_nss_ipv4_sync_queue_init(void)
+{
+	struct nss_ipv4_conn_sync_many_msg *nicsm;
+
+	/*
+	 * Setup the connection sync msg/work/workqueue
+	 */
+	ecm_nss_ipv4_sync_req_msg = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!ecm_nss_ipv4_sync_req_msg) {
+		return false;
+	}
+
+	nss_ipv4_msg_init(ecm_nss_ipv4_sync_req_msg, NSS_IPV4_RX_INTERFACE,
+		NSS_IPV4_TX_CONN_STATS_SYNC_MANY_MSG,
+		sizeof(struct nss_ipv4_conn_sync_many_msg) ,
+		ecm_nss_ipv4_connection_sync_many_callback,
+		NULL);
+
+	nicsm = &ecm_nss_ipv4_sync_req_msg->msg.conn_stats_many;
+
+	/*
+	 * Start with index 0 and calculate the size of the conn stats array
+	 */
+	nicsm->index = 0;
+	nicsm->size = PAGE_SIZE;
+
+	ecm_nss_ipv4_workqueue = create_singlethread_workqueue("ecm_nss_ipv4_workqueue");
+	if (!ecm_nss_ipv4_workqueue) {
+		kfree(ecm_nss_ipv4_sync_req_msg);
+		return false;
+	}
+	INIT_DELAYED_WORK(&ecm_nss_ipv4_work, ecm_nss_ipv4_stats_sync_req_work);
+	queue_delayed_work(ecm_nss_ipv4_workqueue, &ecm_nss_ipv4_work, ECM_NSS_IPV4_STATS_SYNC_PERIOD);
+
+	return true;
+}
+
+/*
  * ecm_nss_ipv4_init()
  */
 int ecm_nss_ipv4_init(struct dentry *dentry)
@@ -2328,6 +2512,12 @@
 		goto task_cleanup;
 	}
 
+	if (!debugfs_create_file("stats_request_counter", S_IRUGO, ecm_nss_ipv4_dentry,
+					NULL, &ecm_nss_ipv4_stats_request_counter_fops)) {
+		DEBUG_ERROR("Failed to create ecm nss ipv4 stats request counter file in debugfs\n");
+		goto task_cleanup;
+	}
+
 #ifdef ECM_NON_PORTED_SUPPORT_ENABLE
 	if (!ecm_nss_non_ported_ipv4_debugfs_init(ecm_nss_ipv4_dentry)) {
 		DEBUG_ERROR("Failed to create ecm non-ported files in debugfs\n");
@@ -2351,6 +2541,11 @@
 		goto task_cleanup;
 	}
 
+	if (!ecm_nss_ipv4_sync_queue_init()) {
+		DEBUG_ERROR("Failed to create ecm ipv4 connection sync workqueue\n");
+		goto task_cleanup;
+	}
+
 #ifdef ECM_MULTICAST_ENABLE
 	ecm_nss_multicast_ipv4_init();
 #endif
@@ -2401,5 +2596,12 @@
 #ifdef ECM_MULTICAST_ENABLE
 	ecm_nss_multicast_ipv4_exit();
 #endif
+
+	/*
+	 * Cancel the conn sync req work and destroy workqueue
+	 */
+	cancel_delayed_work_sync(&ecm_nss_ipv4_work);
+	destroy_workqueue(ecm_nss_ipv4_workqueue);
+	kfree(ecm_nss_ipv4_sync_req_msg);
 }
 EXPORT_SYMBOL(ecm_nss_ipv4_exit);
diff --git a/frontends/nss/ecm_nss_ipv6.c b/frontends/nss/ecm_nss_ipv6.c
index fee8c42..20024de 100644
--- a/frontends/nss/ecm_nss_ipv6.c
+++ b/frontends/nss/ecm_nss_ipv6.c
@@ -112,6 +112,9 @@
 
 #include "ecm_front_end_common.h"
 
+#define ECM_NSS_IPV6_STATS_SYNC_PERIOD msecs_to_jiffies(1000)
+#define ECM_NSS_IPV6_STATS_SYNC_UDELAY 4000	/* Delay for 4ms */
+
 int ecm_nss_ipv6_no_action_limit_default = 250;		/* Default no-action limit. */
 int ecm_nss_ipv6_driver_fail_limit_default = 250;		/* Default driver fail limit. */
 int ecm_nss_ipv6_nack_limit_default = 250;			/* Default nack limit. */
@@ -142,6 +145,17 @@
 bool ecm_nss_ipv6_terminate_pending = false;		/* True when the user has signalled we should quit */
 
 /*
+ * Workqueue for the connection sync
+ */
+struct workqueue_struct *ecm_nss_ipv6_workqueue;
+struct delayed_work ecm_nss_ipv6_work;
+struct nss_ipv6_msg *ecm_nss_ipv6_sync_req_msg;
+static unsigned long int ecm_nss_ipv6_next_req_time;
+static unsigned long int ecm_nss_ipv6_stats_request_success = 0;	/* Number of success stats request */
+static unsigned long int ecm_nss_ipv6_stats_request_fail = 0;		/* Number of failed stats request */
+static unsigned long int ecm_nss_ipv6_stats_request_nack = 0;		/* Number of NACK'd stats request */
+
+/*
  * NSS driver linkage
  */
 struct nss_ctx_instance *ecm_nss_ipv6_nss_ipv6_mgr = NULL;
@@ -1281,12 +1295,11 @@
 }
 
 /*
- * ecm_nss_ipv6_net_dev_callback()
- *	Callback handler from the NSS.
+ * ecm_nss_ipv6_process_one_conn_sync_msg()
+ *	Process one connection sync message.
  */
-static void ecm_nss_ipv6_net_dev_callback(void *app_data, struct nss_ipv6_msg *nim)
+static inline void ecm_nss_ipv6_process_one_conn_sync_msg(struct nss_ipv6_conn_sync *sync)
 {
-	struct nss_ipv6_conn_sync *sync;
 	struct nf_conntrack_tuple_hash *h;
 	struct nf_conntrack_tuple tuple;
 	struct nf_conn *ct;
@@ -1303,15 +1316,6 @@
 	struct in6_addr origin6 __attribute__((unused));
 	struct ecm_classifier_rule_sync class_sync;
 
-	/*
-	 * Only respond to sync messages
-	 */
-	if (nim->cm.type != NSS_IPV6_RX_CONN_STATS_SYNC_MSG) {
-		DEBUG_TRACE("Ignoring nim: %p - not sync: %d", nim, nim->cm.type);
-		return;
-	}
-	sync = &nim->msg.conn_stats;
-
 	ECM_NSS_IPV6_ADDR_TO_IP_ADDR(flow_ip, sync->flow_ip);
 	ECM_NSS_IPV6_ADDR_TO_IP_ADDR(return_ip, sync->return_ip);
 
@@ -1620,6 +1624,110 @@
 }
 
 /*
+ * ecm_nss_ipv6_net_dev_callback()
+ *	Callback handler from the NSS.
+ */
+static void ecm_nss_ipv6_net_dev_callback(void *app_data, struct nss_ipv6_msg *nim)
+{
+	struct nss_ipv6_conn_sync *sync;
+	/*
+	 * Only respond to sync messages
+	 */
+	if (nim->cm.type != NSS_IPV6_RX_CONN_STATS_SYNC_MSG) {
+		DEBUG_TRACE("Ignoring nim: %p - not sync: %d", nim, nim->cm.type);
+		return;
+	}
+	sync = &nim->msg.conn_stats;
+	ecm_nss_ipv6_process_one_conn_sync_msg(sync);
+}
+
+/*
+ * ecm_nss_ipv6_connection_sync_many_callback()
+ *	Callback for conn_sync_many request message
+ */
+static void ecm_nss_ipv6_connection_sync_many_callback(void *app_data, struct nss_ipv6_msg *nim)
+{
+	struct nss_ipv6_conn_sync_many_msg *nicsm = &nim->msg.conn_stats_many;
+	int i;
+
+	/*
+	 * If ECM is terminating, don't process this last stats
+	 */
+	if (ecm_nss_ipv6_terminate_pending) {
+		return;
+	}
+
+	if (nim->cm.response == NSS_CMN_RESPONSE_ACK) {
+		for (i = 0; i < nicsm->count; i++) {
+			ecm_nss_ipv6_process_one_conn_sync_msg(&nicsm->conn_sync[i]);
+		}
+		ecm_nss_ipv6_sync_req_msg->msg.conn_stats_many.index = nicsm->next;
+	} else {
+		/*
+		 * We get a NACK from FW, which should not happen, restart the request
+		 */
+		DEBUG_WARN("IPv6 conn stats request failed, restarting\n");
+		ecm_nss_ipv6_stats_request_nack++;
+		ecm_nss_ipv6_sync_req_msg->msg.conn_stats_many.index = 0;
+	}
+	queue_delayed_work(ecm_nss_ipv6_workqueue, &ecm_nss_ipv6_work, 0);
+}
+
+/*
+ * ecm_nss_ipv6_stats_sync_req_work()
+ *      Schedule delayed work to process connection stats and request next sync
+ */
+static void ecm_nss_ipv6_stats_sync_req_work(struct work_struct *work)
+{
+	/*
+	 * Prepare a nss_ipv6_msg with CONN_STATS_SYNC_MANY request
+	 */
+	struct nss_ipv6_conn_sync_many_msg *nicsm_req;
+	nss_tx_status_t nss_tx_status;
+	int retry = 3;
+	unsigned long int current_jiffies;
+
+	usleep_range(ECM_NSS_IPV6_STATS_SYNC_UDELAY - 100, ECM_NSS_IPV6_STATS_SYNC_UDELAY);
+
+	nicsm_req = &ecm_nss_ipv6_sync_req_msg->msg.conn_stats_many;
+
+	/*
+	 * If index is 0, we are starting a new round, but if we still have time remain
+	 * in this round, sleep until it ends
+	 */
+	if (nicsm_req->index == 0) {
+		current_jiffies = jiffies;
+		if (ecm_nss_ipv6_next_req_time > current_jiffies) {
+			msleep(jiffies_to_msecs(ecm_nss_ipv6_next_req_time - current_jiffies));
+		}
+		ecm_nss_ipv6_next_req_time = jiffies + ECM_NSS_IPV6_STATS_SYNC_PERIOD;
+	}
+
+	while (retry) {
+		if (ecm_nss_ipv6_terminate_pending) {
+			return;
+		}
+		nss_tx_status = nss_ipv6_tx_with_size(ecm_nss_ipv6_nss_ipv6_mgr, ecm_nss_ipv6_sync_req_msg, PAGE_SIZE);
+		if (nss_tx_status == NSS_TX_SUCCESS) {
+			ecm_nss_ipv6_stats_request_success++;
+			return;
+		}
+		ecm_nss_ipv6_stats_request_fail++;
+		retry--;
+		DEBUG_TRACE("TX_NOT_OKAY, try again later\n");
+		usleep_range(100, 200);
+	}
+
+	/*
+	 * TX failed after retries, reschedule ourselves with fresh start
+	 */
+	nicsm_req->count = 0;
+	nicsm_req->index = 0;
+	queue_delayed_work(ecm_nss_ipv6_workqueue, &ecm_nss_ipv6_work, ECM_NSS_IPV6_STATS_SYNC_PERIOD);
+}
+
+
+/*
  * struct nf_hook_ops ecm_nss_ipv6_netfilter_hooks[]
  *	Hooks into netfilter packet monitoring points.
  */
@@ -1967,6 +2075,82 @@
 };
 
 /*
+ * ecm_nss_ipv6_get_stats_request_counter()
+ */
+static ssize_t ecm_nss_ipv6_get_stats_request_counter(struct file *file,
+								char __user *user_buf,
+								size_t sz, loff_t *ppos)
+{
+	char *buf;
+	int ret;
+
+	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!buf) {
+		return -ENOMEM;
+	}
+
+	ret = snprintf(buf, (ssize_t)PAGE_SIZE, "success=%lu\tfail=%lu\tnack=%lu\t\n",
+			ecm_nss_ipv6_stats_request_success, ecm_nss_ipv6_stats_request_fail,
+			ecm_nss_ipv6_stats_request_nack);
+	if (ret < 0) {
+		kfree(buf);
+		return -EFAULT;
+	}
+
+	ret = simple_read_from_buffer(user_buf, sz, ppos, buf, ret);
+	kfree(buf);
+	return ret;
+}
+
+/*
+ * File operations for decel command average time.
+ */
+static struct file_operations ecm_nss_ipv6_stats_request_counter_fops = {
+	.read = ecm_nss_ipv6_get_stats_request_counter,
+};
+
+/*
+ * ecm_nss_ipv6_sync_queue_init
+ *	Initialize the workqueue for ipv6 stats sync
+ */
+static bool ecm_nss_ipv6_sync_queue_init(void)
+{
+	struct nss_ipv6_conn_sync_many_msg *nicsm;
+
+	/*
+	 * Setup the connection sync msg/work/workqueue
+	 */
+	ecm_nss_ipv6_sync_req_msg = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!ecm_nss_ipv6_sync_req_msg) {
+		return false;
+	}
+
+	nss_ipv6_msg_init(ecm_nss_ipv6_sync_req_msg, NSS_IPV6_RX_INTERFACE,
+		NSS_IPV6_TX_CONN_STATS_SYNC_MANY_MSG,
+		sizeof(struct nss_ipv6_conn_sync_many_msg),
+		ecm_nss_ipv6_connection_sync_many_callback,
+		NULL);
+
+	nicsm = &ecm_nss_ipv6_sync_req_msg->msg.conn_stats_many;
+
+	/*
+	 * Start with index 0 and calculate the size of the conn stats array
+	 */
+	nicsm->index = 0;
+	nicsm->size = PAGE_SIZE;
+
+	ecm_nss_ipv6_workqueue = create_singlethread_workqueue("ecm_nss_ipv6_workqueue");
+	if (!ecm_nss_ipv6_workqueue) {
+		kfree(ecm_nss_ipv6_sync_req_msg);
+		return false;
+	}
+	INIT_DELAYED_WORK(&ecm_nss_ipv6_work, ecm_nss_ipv6_stats_sync_req_work);
+	queue_delayed_work(ecm_nss_ipv6_workqueue, &ecm_nss_ipv6_work, ECM_NSS_IPV6_STATS_SYNC_PERIOD);
+
+	return true;
+}
+
+/*
  * ecm_nss_ipv6_init()
  */
 int ecm_nss_ipv6_init(struct dentry *dentry)
@@ -2046,6 +2230,12 @@
 		goto task_cleanup;
 	}
 
+	if (!debugfs_create_file("stats_request_counter", S_IRUGO, ecm_nss_ipv6_dentry,
+					NULL, &ecm_nss_ipv6_stats_request_counter_fops)) {
+		DEBUG_ERROR("Failed to create ecm nss ipv6 stats_request_counter file in debugfs\n");
+		goto task_cleanup;
+	}
+
 #ifdef ECM_NON_PORTED_SUPPORT_ENABLE
 	if (!ecm_nss_non_ported_ipv6_debugfs_init(ecm_nss_ipv6_dentry)) {
 		DEBUG_ERROR("Failed to create ecm non-ported files in debugfs\n");
@@ -2053,6 +2243,11 @@
 	}
 #endif
 
+	if (!ecm_nss_ipv6_sync_queue_init()) {
+		DEBUG_ERROR("Failed to create ecm ipv6 connection sync workqueue\n");
+		goto task_cleanup;
+	}
+
 #ifdef ECM_MULTICAST_ENABLE
 	if (!ecm_nss_multicast_ipv6_debugfs_init(ecm_nss_ipv6_dentry)) {
 		DEBUG_ERROR("Failed to create ecm multicast files in debugfs\n");
@@ -2118,5 +2313,12 @@
 #ifdef ECM_MULTICAST_ENABLE
 	ecm_nss_multicast_ipv6_exit();
 #endif
+
+	/*
+	 * Cancel the conn sync req work and destroy workqueue
+	 */
+	cancel_delayed_work_sync(&ecm_nss_ipv6_work);
+	destroy_workqueue(ecm_nss_ipv6_workqueue);
+	kfree(ecm_nss_ipv6_sync_req_msg);
 }
 EXPORT_SYMBOL(ecm_nss_ipv6_exit);