Merge "[qca-nss-ecm] Conntrack cleans up DB when route event happens"
diff --git a/ecm_db.c b/ecm_db.c
index 1e677e9..9cf7765 100644
--- a/ecm_db.c
+++ b/ecm_db.c
@@ -83,6 +83,13 @@
 #endif
 
 /*
+ * Check the configured HZ value.
+ */
+#if HZ > 100000
+#error "Bad HZ value"
+#endif
+
+/*
  * Global lists.
  * All instances are inserted into global list - this allows easy iteration of all instances of a particular type.
  * The list is doubly linked for fast removal.  The list is in no particular order.
@@ -1036,6 +1043,46 @@
 }
 
 /*
+ * ecm_db_connection_elapsed_defunct_timer()
+ *	Returns the elapsed time of defunct timer.
+ * If the timer is already expired and not removed from the database, the
+ * function returns a negative value. The caller MUST handle this return value.
+ */
+int ecm_db_connection_elapsed_defunct_timer(struct ecm_db_connection_instance *ci)
+{
+	long int expires_in;
+	int elapsed;
+
+	DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
+
+	/*
+	 * Do some sanity checks.
+	 * If it is not in a timer group, which means already expired, or the
+	 * connection has not been fully created yet. Just return 0.
+	 */
+	spin_lock_bh(&ecm_db_lock);
+	if (ci->defunct_timer.group == ECM_DB_TIMER_GROUPS_MAX) {
+		spin_unlock_bh(&ecm_db_lock);
+		return -1;
+	}
+
+	/*
+	 * Already expired, but not removed from the database completely.
+	 */
+	expires_in = (long int)(ci->defunct_timer.timeout - ecm_db_time);
+	if (expires_in < 0) {
+		spin_unlock_bh(&ecm_db_lock);
+		return -1;
+	}
+
+	elapsed = ecm_db_timer_groups[ci->defunct_timer.group].time - expires_in;
+	spin_unlock_bh(&ecm_db_lock);
+
+	return elapsed;
+}
+EXPORT_SYMBOL(ecm_db_connection_elapsed_defunct_timer);
+
+/*
  * ecm_db_connection_defunct_timer_reset()
  *	Set/change the timer group associated with a connection.  Returns false if the connection has become defunct and the new group cannot be set for that reason.
  */
@@ -1079,14 +1126,31 @@
  */
 void ecm_db_connection_make_defunct(struct ecm_db_connection_instance *ci)
 {
+	struct ecm_front_end_connection_instance *feci;
+	ecm_front_end_acceleration_mode_t accel_mode;
+
 	DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
 
 	if (ci->defunct) {
 		ci->defunct(ci->feci);
 	}
 
-	if (ecm_db_timer_group_entry_remove(&ci->defunct_timer)) {
-		ecm_db_connection_deref(ci);
+	feci = ecm_db_connection_front_end_get_and_ref(ci);
+	accel_mode = feci->accel_state_get(feci);
+	feci->deref(feci);
+
+	/*
+	 * It is possible that the defunct process fails and re-try is in progress.
+	 * In that case, the connection's defunct timer is reset to defunct re-try
+	 * timeout value and the connection waits for the next defunct call. So, we
+	 * should remove the timer from the timer group, if the re-acceleration for this
+	 * connection is not possible which means "decel pending" or one of the
+	 * "accel fail" modes. Otherwise, the timer will be removed and re-try will not happen.
+	 */
+	if (ECM_FRONT_END_ACCELERATION_NOT_POSSIBLE(accel_mode)) {
+		if (ecm_db_timer_group_entry_remove(&ci->defunct_timer)) {
+			ecm_db_connection_deref(ci);
+		}
 	}
 }
 EXPORT_SYMBOL(ecm_db_connection_make_defunct);
@@ -12409,6 +12473,12 @@
 	ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_SIP_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_SIP_TIMEOUT;
 
 	/*
+	 * Defunct re-try timeout (5 seconds)
+	 */
+	ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_DEFUNCT_RETRY_TIMEOUT].time = ECM_DB_CONNECTION_DEFUNCT_RETRY_TIMEOUT;
+	ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_DEFUNCT_RETRY_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_DEFUNCT_RETRY_TIMEOUT;
+
+	/*
 	 * Reset connection by protocol counters
 	 */
 	memset(ecm_db_connection_count_by_protocol, 0, sizeof(ecm_db_connection_count_by_protocol));
diff --git a/ecm_db.h b/ecm_db.h
index d74d86f..39c9e7e 100644
--- a/ecm_db.h
+++ b/ecm_db.h
@@ -38,6 +38,7 @@
 void ecm_db_connection_data_totals_update_dropped(struct ecm_db_connection_instance *ci, bool is_from, uint64_t size, uint64_t packets);
 void ecm_db_connection_data_totals_update(struct ecm_db_connection_instance *ci, bool is_from, uint64_t size, uint64_t packets);
 
+int ecm_db_connection_elapsed_defunct_timer(struct ecm_db_connection_instance *ci);
 bool ecm_db_connection_defunct_timer_reset(struct ecm_db_connection_instance *ci, ecm_db_timer_group_t tg);
 bool ecm_db_connection_defunct_timer_touch(struct ecm_db_connection_instance *ci);
 void ecm_db_connection_make_defunct(struct ecm_db_connection_instance *ci);
diff --git a/ecm_db_types.h b/ecm_db_types.h
index 1ad4ff1..cbe3b69 100644
--- a/ecm_db_types.h
+++ b/ecm_db_types.h
@@ -105,6 +105,7 @@
 #define ECM_DB_CONNECTION_SDP_TIMEOUT 120
 #define ECM_DB_CONNECTION_SIP_TIMEOUT 28800
 #define ECM_DB_CONNECTION_BITTORRENT_TIMEOUT 120
+#define ECM_DB_CONNECTION_DEFUNCT_RETRY_TIMEOUT 5
 
 /*
  * Timer groups.
@@ -135,6 +136,7 @@
 	ECM_DB_TIMER_GROUPS_CONNECTION_SIP_TIMEOUT,		/* SIP timeout */
 	ECM_DB_TIMER_GROUPS_CONNECTION_IGMP_TIMEOUT,		/* IGMP timeout */
 	ECM_DB_TIMER_GROUPS_CONNECTION_BITTORRENT_TIMEOUT,	/* Bittorrent connections timeout */
+	ECM_DB_TIMER_GROUPS_CONNECTION_DEFUNCT_RETRY_TIMEOUT,	/* Defunct retry timeout */
 	ECM_DB_TIMER_GROUPS_MAX					/* Always the last one */
 };
 typedef enum ecm_db_timer_groups ecm_db_timer_group_t;
diff --git a/frontends/ecm_front_end_common.c b/frontends/ecm_front_end_common.c
index fe2e5e5..6ed9d4b 100644
--- a/frontends/ecm_front_end_common.c
+++ b/frontends/ecm_front_end_common.c
@@ -80,4 +80,38 @@
 }
 #endif
 
+/*
+ * ecm_front_end_destroy_failure_handle()
+ *	Destroy request failure handler.
+ */
+void ecm_front_end_destroy_failure_handle(struct ecm_front_end_connection_instance *feci)
+{
+	spin_lock_bh(&feci->lock);
+	feci->stats.driver_fail_total++;
+	feci->stats.driver_fail++;
+	if (feci->stats.driver_fail >= feci->stats.driver_fail_limit) {
+		/*
+		 * Reached to the driver failure limit. ECM no longer allows
+		 * re-trying deceleration.
+		 */
+		feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DRIVER;
+		spin_unlock_bh(&feci->lock);
+		DEBUG_WARN("%p: Decel failed - driver fail limit\n", feci);
+		return;
+	}
 
+	/*
+	 * Destroy request failed. The accelerated connection couldn't be destroyed
+	 * in the acceleration engine. Revert back the accel_mode, unset the is_defunct
+	 * flag just in case this request has come through the defunct process.
+	 */
+	feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_ACCEL;
+	feci->is_defunct = false;
+	spin_unlock_bh(&feci->lock);
+
+	/*
+	 * Reset the defunct timer to a smaller timeout value so that the connection will be
+	 * tried to be defuncted again, when the timeout expires (its value is 5 seconds).
+	 */
+	ecm_db_connection_defunct_timer_reset(feci->ci, ECM_DB_TIMER_GROUPS_CONNECTION_DEFUNCT_RETRY_TIMEOUT);
+}
diff --git a/frontends/include/ecm_front_end_common.h b/frontends/include/ecm_front_end_common.h
index 8a9ed93..ca3a35a 100644
--- a/frontends/include/ecm_front_end_common.h
+++ b/frontends/include/ecm_front_end_common.h
@@ -171,3 +171,4 @@
 extern void ecm_front_end_bond_notifier_stop(int num);
 extern int ecm_front_end_bond_notifier_init(struct dentry *dentry);
 extern void ecm_front_end_bond_notifier_exit(void);
+extern void ecm_front_end_destroy_failure_handle(struct ecm_front_end_connection_instance *feci);
diff --git a/frontends/nss/ecm_nss_ipv4.c b/frontends/nss/ecm_nss_ipv4.c
index a82cc02..b5d3d7a 100644
--- a/frontends/nss/ecm_nss_ipv4.c
+++ b/frontends/nss/ecm_nss_ipv4.c
@@ -1721,6 +1721,8 @@
 	struct ecm_classifier_rule_sync class_sync;
 	int flow_dir;
 	int return_dir;
+	unsigned long int delta_jiffies;
+	int elapsed;
 
 	/*
 	 * Look up ecm connection with a view to synchronising the connection, classifier and data tracker.
@@ -1764,11 +1766,26 @@
 #endif
 	if (!ci) {
 		DEBUG_TRACE("%p: NSS Sync: no connection\n", sync);
-		goto sync_conntrack;
+		return;
 	}
+
 	DEBUG_TRACE("%p: Sync conn %p\n", sync, ci);
 
 	/*
+	 * Get the elapsed time since the last sync and add this elapsed time
+	 * to the conntrack's timeout while updating it. If the return value is
+	 * a negative value which means the timer is not in a valid state, just
+	 * return here and do not update the defunct timer and the conntrack.
+	 */
+	elapsed = ecm_db_connection_elapsed_defunct_timer(ci);
+	if (elapsed < 0) {
+		ecm_db_connection_deref(ci);
+		return;
+	}
+	DEBUG_TRACE("%p: elapsed: %d\n", ci, elapsed);
+	delta_jiffies = elapsed * HZ;
+
+	/*
 	 * Keep connection alive and updated
 	 */
 	if (!ecm_db_connection_defunct_timer_touch(ci)) {
@@ -2007,19 +2024,9 @@
 
 	/*
 	 * Only update if this is not a fixed timeout
+	 * delta_jiffies is the elapsed time since the last sync of this connection.
 	 */
 	if (!test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) {
-		unsigned long int delta_jiffies;
-
-		/*
-		 * Convert ms ticks from the NSS to jiffies.  We know that inc_ticks is small
-		 * and we expect HZ to be small too so we can multiply without worrying about
-		 * wrap-around problems.  We add a rounding constant to ensure that the different
-		 * time bases don't cause truncation errors.
-		 */
-		DEBUG_ASSERT(HZ <= 100000, "Bad HZ\n");
-		delta_jiffies = ((sync->inc_ticks * HZ) + (MSEC_PER_SEC / 2)) / MSEC_PER_SEC;
-
 		spin_lock_bh(&ct->lock);
 		ct->timeout.expires += delta_jiffies;
 		spin_unlock_bh(&ct->lock);
diff --git a/frontends/nss/ecm_nss_ipv6.c b/frontends/nss/ecm_nss_ipv6.c
index f43ee59..d420317 100644
--- a/frontends/nss/ecm_nss_ipv6.c
+++ b/frontends/nss/ecm_nss_ipv6.c
@@ -1387,6 +1387,8 @@
 	struct ecm_classifier_rule_sync class_sync;
 	int flow_dir;
 	int return_dir;
+	unsigned long int delta_jiffies;
+	int elapsed;
 
 	ECM_NSS_IPV6_ADDR_TO_IP_ADDR(flow_ip, sync->flow_ip);
 	ECM_NSS_IPV6_ADDR_TO_IP_ADDR(return_ip, sync->return_ip);
@@ -1408,11 +1410,25 @@
 	ci = ecm_db_connection_find_and_ref(flow_ip, return_ip, sync->protocol, (int)sync->flow_ident, (int)sync->return_ident);
 	if (!ci) {
 		DEBUG_TRACE("%p: NSS Sync: no connection\n", sync);
-		goto sync_conntrack;
+		return;
 	}
+
 	DEBUG_TRACE("%p: Sync conn %p\n", sync, ci);
 
 	/*
+	 * Get the elapsed time since the last sync and add this elapsed time
+	 * to the conntrack's timeout while updating it. If the return value is
+	 * a negative value which means the timer is not in a valid state, just
+	 * return here and do not update the defunct timer and the conntrack.
+	 */
+	elapsed = ecm_db_connection_elapsed_defunct_timer(ci);
+	if (elapsed < 0) {
+		ecm_db_connection_deref(ci);
+		return;
+	}
+	delta_jiffies = elapsed * HZ;
+
+	/*
 	 * Keep connection alive and updated
 	 */
 	if (!ecm_db_connection_defunct_timer_touch(ci)) {
@@ -1650,23 +1666,14 @@
 
 	/*
 	 * Only update if this is not a fixed timeout
+	 * delta_jiffies is the elapsed time since the last sync of this connection.
 	 */
 	if (!test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) {
-		unsigned long int delta_jiffies;
-
-		/*
-		 * Convert ms ticks from the NSS to jiffies. We know that inc_ticks is small
-		 * and we expect HZ to be small too so we can multiply without worrying about
-		 * wrap-around problems. We add a rounding constant to ensure that the different
-		 * time bases don't cause truncation errors.
-		 */
-		DEBUG_ASSERT(HZ <= 100000, "Bad HZ\n");
-		delta_jiffies = ((sync->inc_ticks * HZ) + (MSEC_PER_SEC / 2)) / MSEC_PER_SEC;
-
 		spin_lock_bh(&ct->lock);
 		ct->timeout.expires += delta_jiffies;
 		spin_unlock_bh(&ct->lock);
 	}
+
 #if (LINUX_VERSION_CODE <= KERNEL_VERSION(3,6,0))
 	acct = nf_conn_acct_find(ct);
 #else
diff --git a/frontends/nss/ecm_nss_non_ported_ipv4.c b/frontends/nss/ecm_nss_non_ported_ipv4.c
index 0586594..6e1eee0 100644
--- a/frontends/nss/ecm_nss_non_ported_ipv4.c
+++ b/frontends/nss/ecm_nss_non_ported_ipv4.c
@@ -74,6 +74,7 @@
 #include "ecm_tracker.h"
 #include "ecm_classifier.h"
 #include "ecm_front_end_types.h"
+#include "ecm_front_end_common.h"
 #include "ecm_tracker_datagram.h"
 #include "ecm_tracker_udp.h"
 #include "ecm_tracker_tcp.h"
@@ -1456,14 +1457,7 @@
 	/*
 	 * TX failed
 	 */
-	spin_lock_bh(&feci->lock);
-	feci->stats.driver_fail_total++;
-	feci->stats.driver_fail++;
-	if (feci->stats.driver_fail >= feci->stats.driver_fail_limit) {
-		DEBUG_WARN("%p: Decel failed - driver fail limit\n", nnpci);
-		feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DRIVER;
-	}
-	spin_unlock_bh(&feci->lock);
+	ecm_front_end_destroy_failure_handle(feci);
 
 	/*
 	 * Could not send the request, decrement the decel pending counter
diff --git a/frontends/nss/ecm_nss_non_ported_ipv6.c b/frontends/nss/ecm_nss_non_ported_ipv6.c
index c5efaaf..3e91a62 100644
--- a/frontends/nss/ecm_nss_non_ported_ipv6.c
+++ b/frontends/nss/ecm_nss_non_ported_ipv6.c
@@ -75,6 +75,7 @@
 #include "ecm_tracker.h"
 #include "ecm_classifier.h"
 #include "ecm_front_end_types.h"
+#include "ecm_front_end_common.h"
 #include "ecm_tracker_datagram.h"
 #include "ecm_tracker_udp.h"
 #include "ecm_tracker_tcp.h"
@@ -1274,14 +1275,7 @@
 	/*
 	 * TX failed
 	 */
-	spin_lock_bh(&feci->lock);
-	feci->stats.driver_fail_total++;
-	feci->stats.driver_fail++;
-	if (feci->stats.driver_fail >= feci->stats.driver_fail_limit) {
-		DEBUG_WARN("%p: Decel failed - driver fail limit\n", nnpci);
-		feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DRIVER;
-	}
-	spin_unlock_bh(&feci->lock);
+	ecm_front_end_destroy_failure_handle(feci);
 
 	/*
 	 * Could not send the request, decrement the decel pending counter
diff --git a/frontends/nss/ecm_nss_ported_ipv4.c b/frontends/nss/ecm_nss_ported_ipv4.c
index 71d87e9..44f24ee 100644
--- a/frontends/nss/ecm_nss_ported_ipv4.c
+++ b/frontends/nss/ecm_nss_ported_ipv4.c
@@ -1487,14 +1487,7 @@
 	/*
 	 * TX failed
 	 */
-	spin_lock_bh(&feci->lock);
-	feci->stats.driver_fail_total++;
-	feci->stats.driver_fail++;
-	if (feci->stats.driver_fail >= feci->stats.driver_fail_limit) {
-		DEBUG_WARN("%p: Decel failed - driver fail limit\n", npci);
-		feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DRIVER;
-	}
-	spin_unlock_bh(&feci->lock);
+	ecm_front_end_destroy_failure_handle(feci);
 
 	/*
 	 * Could not send the request, decrement the decel pending counter
diff --git a/frontends/nss/ecm_nss_ported_ipv6.c b/frontends/nss/ecm_nss_ported_ipv6.c
index 1bb31f0..08641cb 100644
--- a/frontends/nss/ecm_nss_ported_ipv6.c
+++ b/frontends/nss/ecm_nss_ported_ipv6.c
@@ -1377,14 +1377,7 @@
 	/*
 	 * TX failed
 	 */
-	spin_lock_bh(&feci->lock);
-	feci->stats.driver_fail_total++;
-	feci->stats.driver_fail++;
-	if (feci->stats.driver_fail >= feci->stats.driver_fail_limit) {
-		DEBUG_WARN("%p: Decel failed - driver fail limit\n", npci);
-		feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DRIVER;
-	}
-	spin_unlock_bh(&feci->lock);
+	ecm_front_end_destroy_failure_handle(feci);
 
 	/*
 	 * Could not send the request, decrement the decel pending counter
diff --git a/frontends/sfe/ecm_sfe_ipv4.c b/frontends/sfe/ecm_sfe_ipv4.c
index 03c7c4c..701c949 100644
--- a/frontends/sfe/ecm_sfe_ipv4.c
+++ b/frontends/sfe/ecm_sfe_ipv4.c
@@ -1533,12 +1533,11 @@
 		unsigned long int delta_jiffies;
 
 		/*
-		 * Convert ms ticks from the SFE to jiffies.  We know that inc_ticks is small
+		 * Convert ms ticks from the SFE to jiffies. We know that inc_ticks is small
 		 * and we expect HZ to be small too so we can multiply without worrying about
 		 * wrap-around problems.  We add a rounding constant to ensure that the different
 		 * time bases don't cause truncation errors.
 		 */
-		DEBUG_ASSERT(HZ <= 100000, "Bad HZ\n");
 		delta_jiffies = ((sync->inc_ticks * HZ) + (MSEC_PER_SEC / 2)) / MSEC_PER_SEC;
 
 		spin_lock_bh(&ct->lock);
diff --git a/frontends/sfe/ecm_sfe_ipv6.c b/frontends/sfe/ecm_sfe_ipv6.c
index 96153a8..927bb89 100644
--- a/frontends/sfe/ecm_sfe_ipv6.c
+++ b/frontends/sfe/ecm_sfe_ipv6.c
@@ -1247,12 +1247,11 @@
 		unsigned long int delta_jiffies;
 
 		/*
-		 * Convert ms ticks from the SFE to jiffies.  We know that inc_ticks is small
+		 * Convert ms ticks from the SFE to jiffies. We know that inc_ticks is small
 		 * and we expect HZ to be small too so we can multiply without worrying about
 		 * wrap-around problems.  We add a rounding constant to ensure that the different
 		 * time bases don't cause truncation errors.
 		 */
-		DEBUG_ASSERT(HZ <= 100000, "Bad HZ\n");
 		delta_jiffies = ((sync->inc_ticks * HZ) + (MSEC_PER_SEC / 2)) / MSEC_PER_SEC;
 
 		spin_lock_bh(&ct->lock);
diff --git a/frontends/sfe/ecm_sfe_non_ported_ipv4.c b/frontends/sfe/ecm_sfe_non_ported_ipv4.c
index 7008b7a..4160bd4 100644
--- a/frontends/sfe/ecm_sfe_non_ported_ipv4.c
+++ b/frontends/sfe/ecm_sfe_non_ported_ipv4.c
@@ -74,6 +74,7 @@
 #include "ecm_tracker.h"
 #include "ecm_classifier.h"
 #include "ecm_front_end_types.h"
+#include "ecm_front_end_common.h"
 #include "ecm_tracker_datagram.h"
 #include "ecm_tracker_udp.h"
 #include "ecm_tracker_tcp.h"
@@ -1354,14 +1355,7 @@
 	/*
 	 * TX failed
 	 */
-	spin_lock_bh(&feci->lock);
-	feci->stats.driver_fail_total++;
-	feci->stats.driver_fail++;
-	if (feci->stats.driver_fail >= feci->stats.driver_fail_limit) {
-		DEBUG_WARN("%p: Decel failed - driver fail limit\n", nnpci);
-		feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DRIVER;
-	}
-	spin_unlock_bh(&feci->lock);
+	ecm_front_end_destroy_failure_handle(feci);
 
 	/*
 	 * Could not send the request, decrement the decel pending counter
diff --git a/frontends/sfe/ecm_sfe_non_ported_ipv6.c b/frontends/sfe/ecm_sfe_non_ported_ipv6.c
index 89ab791..c0e082f 100644
--- a/frontends/sfe/ecm_sfe_non_ported_ipv6.c
+++ b/frontends/sfe/ecm_sfe_non_ported_ipv6.c
@@ -75,6 +75,7 @@
 #include "ecm_tracker.h"
 #include "ecm_classifier.h"
 #include "ecm_front_end_types.h"
+#include "ecm_front_end_common.h"
 #include "ecm_tracker_datagram.h"
 #include "ecm_tracker_udp.h"
 #include "ecm_tracker_tcp.h"
@@ -1245,14 +1246,7 @@
 	/*
 	 * TX failed
 	 */
-	spin_lock_bh(&feci->lock);
-	feci->stats.driver_fail_total++;
-	feci->stats.driver_fail++;
-	if (feci->stats.driver_fail >= feci->stats.driver_fail_limit) {
-		DEBUG_WARN("%p: Decel failed - driver fail limit\n", nnpci);
-		feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DRIVER;
-	}
-	spin_unlock_bh(&feci->lock);
+	ecm_front_end_destroy_failure_handle(feci);
 
 	/*
 	 * Could not send the request, decrement the decel pending counter
diff --git a/frontends/sfe/ecm_sfe_ported_ipv4.c b/frontends/sfe/ecm_sfe_ported_ipv4.c
index 7356ed4..896747b 100644
--- a/frontends/sfe/ecm_sfe_ported_ipv4.c
+++ b/frontends/sfe/ecm_sfe_ported_ipv4.c
@@ -1395,14 +1395,7 @@
 	/*
 	 * TX failed
 	 */
-	spin_lock_bh(&feci->lock);
-	feci->stats.driver_fail_total++;
-	feci->stats.driver_fail++;
-	if (feci->stats.driver_fail >= feci->stats.driver_fail_limit) {
-		DEBUG_WARN("%p: Decel failed - driver fail limit\n", npci);
-		feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DRIVER;
-	}
-	spin_unlock_bh(&feci->lock);
+	ecm_front_end_destroy_failure_handle(feci);
 
 	/*
 	 * Could not send the request, decrement the decel pending counter
diff --git a/frontends/sfe/ecm_sfe_ported_ipv6.c b/frontends/sfe/ecm_sfe_ported_ipv6.c
index aab8267..ae9a9f8 100644
--- a/frontends/sfe/ecm_sfe_ported_ipv6.c
+++ b/frontends/sfe/ecm_sfe_ported_ipv6.c
@@ -1338,14 +1338,7 @@
 	/*
 	 * TX failed
 	 */
-	spin_lock_bh(&feci->lock);
-	feci->stats.driver_fail_total++;
-	feci->stats.driver_fail++;
-	if (feci->stats.driver_fail >= feci->stats.driver_fail_limit) {
-		DEBUG_WARN("%p: Decel failed - driver fail limit\n", npci);
-		feci->accel_mode = ECM_FRONT_END_ACCELERATION_MODE_FAIL_DRIVER;
-	}
-	spin_unlock_bh(&feci->lock);
+	ecm_front_end_destroy_failure_handle(feci);
 
 	/*
 	 * Could not send the request, decrement the decel pending counter