diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 4121c5c9bb..02757f34de 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -53,6 +53,11 @@
 #ifdef RTE_LIB_GRO
 #include <rte_gro.h>
 #endif
+#ifdef RTE_NET_BOND
+#include <rte_eth_bond.h>
+#include <rte_eth_bond_8023ad.h>
+#endif
+
 #include <rte_hexdump.h>
 
 #include "testpmd.h"
@@ -4439,6 +4444,17 @@ fwd_topology_tx_port_get(portid_t rxp)
 	}
 }
 
+static inline void
+fwd_config_bond4_send_periodical_lacp(struct fwd_stream *fwd_stream)
+{
+#ifdef RTE_NET_BOND
+	if (rte_eth_bond_8023ad_dedicated_queues_get(fwd_stream->tx_port) == 0)
+		fwd_stream->bond4_send_periodical_lacp = true;
+#else
+	RTE_SET_USED(fwd_stream);
+#endif
+}
+
 static void
 simple_fwd_config_setup(void)
 {
@@ -4469,6 +4485,7 @@ simple_fwd_config_setup(void)
 		fwd_streams[i]->tx_queue  = 0;
 		fwd_streams[i]->peer_addr = fwd_streams[i]->tx_port;
 		fwd_streams[i]->retry_enabled = retry_enabled;
+		fwd_config_bond4_send_periodical_lacp(fwd_streams[i]);
 	}
 }
 
@@ -4530,6 +4547,7 @@ rss_fwd_config_setup(void)
 		fs->tx_queue = rxq;
 		fs->peer_addr = fs->tx_port;
 		fs->retry_enabled = retry_enabled;
+		fwd_config_bond4_send_periodical_lacp(fs);
 		rxp++;
 		if (rxp < nb_fwd_ports)
 			continue;
@@ -4645,6 +4663,7 @@ dcb_fwd_config_setup(void)
 				fs->tx_queue = txq + j % nb_tx_queue;
 				fs->peer_addr = fs->tx_port;
 				fs->retry_enabled = retry_enabled;
+				fwd_config_bond4_send_periodical_lacp(fs);
 			}
 			fwd_lcores[lc_id]->stream_nb +=
 				rxp_dcb_info.tc_queue.tc_rxq[i][tc].nb_queue;
diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index e734ad9a02..0fee5c5c94 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -205,6 +205,7 @@ usage(char* progname)
 	printf("  --hairpin-mode=0xXX: bitmask set the hairpin port mode.\n"
 	       "    0x10 - explicit Tx rule, 0x02 - hairpin ports paired\n"
 	       "    0x01 - hairpin ports loop, 0x00 - hairpin port self\n");
+	printf("  --bond4-lacp-fwd: enable LACP update in fwd main loop\n");
 }
 
 #ifdef RTE_LIB_CMDLINE
@@ -705,6 +706,7 @@ launch_args_parse(int argc, char** argv)
 		{ "rx-mq-mode",                 1, 0, 0 },
 		{ "record-core-cycles",         0, 0, 0 },
 		{ "record-burst-stats",         0, 0, 0 },
+		{ "bond4-lacp-fwd",             0, 0, 0 },
 		{ PARAM_NUM_PROCS,              1, 0, 0 },
 		{ PARAM_PROC_ID,                1, 0, 0 },
 		{ 0, 0, 0, 0 },
@@ -1462,6 +1464,8 @@ launch_args_parse(int argc, char** argv)
 				num_procs = atoi(optarg);
 			if (!strcmp(lgopts[opt_idx].name, PARAM_PROC_ID))
 				proc_id = atoi(optarg);
+			if (!strcmp(lgopts[opt_idx].name, "bond4-lacp-fwd"))
+				bond4_lacp_fwd = 1;
 			break;
 		case 'h':
 			usage(argv[0]);
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index 0c14325b8d..7755243cd6 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -68,6 +68,7 @@
 #endif
 #ifdef RTE_NET_BOND
 #include <rte_eth_bond.h>
+#include <rte_eth_bond_8023ad.h>
 #endif
 #ifdef RTE_NET_MLX5
 #include "mlx5_testpmd.h"
@@ -519,6 +520,9 @@ struct gro_status gro_ports[RTE_MAX_ETHPORTS];
 uint8_t gro_flush_cycles = GRO_DEFAULT_FLUSH_CYCLES;
 #endif
 
+uint8_t bond4_lacp_fwd;
+#define LACP_UPDATE_PERIOD 10000
+
 /*
  * hexadecimal bitmask of RX mq mode can be enabled.
  */
@@ -2252,6 +2256,28 @@ flush_fwd_rx_queues(void)
 	}
 }
 
+static inline void
+try_lacp_send_in_fwd(struct fwd_stream **fsm, streamid_t nb_fs)
+{
+#ifdef RTE_NET_BOND
+	void *qd;
+	streamid_t sm_id;
+	struct rte_eth_fp_ops *p;
+
+	for (sm_id = 0; sm_id < nb_fs; sm_id++) {
+		/* Update bond4 LACP if dedicated queues disabled. */
+		if (fsm[sm_id]->bond4_send_periodical_lacp) {
+			p = &rte_eth_fp_ops[fsm[sm_id]->tx_port];
+			qd = p->txq.data[fsm[sm_id]->tx_queue];
+			rte_eth_bond_8023ad_lacp_send_one(qd);
+		}
+	}
+#else
+	RTE_SET_USED(fsm);
+	RTE_SET_USED(nb_fs);
+#endif
+}
+
 static void
 run_pkt_fwd_on_lcore(struct fwd_lcore *fc, packet_fwd_t pkt_fwd)
 {
@@ -2269,6 +2295,9 @@ run_pkt_fwd_on_lcore(struct fwd_lcore *fc, packet_fwd_t pkt_fwd)
 	tics_datum = rte_rdtsc();
 	tics_per_1sec = rte_get_timer_hz();
 #endif
+	uint64_t before_tsc = rte_rdtsc();
+	const uint64_t bond4_lacp_period = (rte_get_tsc_hz() + US_PER_S - 1) /
+			US_PER_S * LACP_UPDATE_PERIOD;
 	fsm = &fwd_streams[fc->stream_idx];
 	nb_fs = fc->stream_nb;
 	prev_tsc = rte_rdtsc();
@@ -2300,6 +2329,13 @@ run_pkt_fwd_on_lcore(struct fwd_lcore *fc, packet_fwd_t pkt_fwd)
 			fc->total_cycles += tsc - prev_tsc;
 			prev_tsc = tsc;
 		}
+		if (bond4_lacp_fwd != 0) {
+			uint64_t current_tsc = rte_rdtsc();
+			if (unlikely((current_tsc - before_tsc) > bond4_lacp_period)) {
+				try_lacp_send_in_fwd(fsm, nb_fs);
+				before_tsc = current_tsc;
+			}
+		}
 	} while (! fc->stopped);
 }
 
@@ -4462,6 +4498,7 @@ main(int argc, char** argv)
 #ifdef RTE_LIB_LATENCYSTATS
 	latencystats_enabled = 0;
 #endif
+	bond4_lacp_fwd = 0;
 
 	/* on FreeBSD, mlockall() is disabled by default */
 #ifdef RTE_EXEC_ENV_FREEBSD
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 329a6378a1..8f88223069 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -175,6 +175,8 @@ struct fwd_stream {
 	unsigned int gro_times;	/**< GRO operation times */
 #endif
 	uint64_t busy_cycles; /**< used with --record-core-cycles */
+	bool bond4_send_periodical_lacp;
+	/**< Send LACP packets periodically in forward loop */
 	struct pkt_burst_stats rx_burst_stats;
 	struct pkt_burst_stats tx_burst_stats;
 	struct fwd_lcore *lcore; /**< Lcore being scheduled. */
@@ -583,6 +585,8 @@ extern lcoreid_t bitrate_lcore_id;
 extern uint8_t bitrate_enabled;
 #endif
 
+extern uint8_t bond4_lacp_fwd;
+
 extern uint32_t max_rx_pkt_len;
 
 /*
diff --git a/doc/guides/testpmd_app_ug/run_app.rst b/doc/guides/testpmd_app_ug/run_app.rst
index f85f323033..8ea8aefde6 100644
--- a/doc/guides/testpmd_app_ug/run_app.rst
+++ b/doc/guides/testpmd_app_ug/run_app.rst
@@ -538,6 +538,10 @@ The command line options are:
 
     Enable display of RX and TX burst stats.
 
+*   ``--bond4-lacp-fwd``
+
+    Enable LACP packets sending in main forward loop to avoid LACP negotiation failed.
+
 *   ``--hairpin-mode=0xXXXX``
 
     Set the hairpin port configuration with bitmask, only valid when hairpin queues number is set::
