diff --git a/examples/l3fwd/Makefile b/examples/l3fwd/Makefile
index c55f5c288..278586fa1 100644
--- a/examples/l3fwd/Makefile
+++ b/examples/l3fwd/Makefile
@@ -52,6 +52,7 @@ include $(RTE_SDK)/mk/rte.vars.mk
 CFLAGS += -I$(SRCDIR)
 CFLAGS += -O3 $(USER_FLAGS)
 CFLAGS += $(WERROR_FLAGS)
+CFLAGS += -DALLOW_EXPERIMENTAL_API
 
 include $(RTE_SDK)/mk/rte.extapp.mk
 endif
diff --git a/examples/l3fwd/l3fwd_lpm.c b/examples/l3fwd/l3fwd_lpm.c
index 1435a5711..33ee04c0e 100644
--- a/examples/l3fwd/l3fwd_lpm.c
+++ b/examples/l3fwd/l3fwd_lpm.c
@@ -21,6 +21,7 @@
 #include <rte_ethdev.h>
 #include <rte_cycles.h>
 #include <rte_mbuf.h>
+#include <rte_malloc.h>
 #include <rte_ip.h>
 #include <rte_tcp.h>
 #include <rte_udp.h>
@@ -77,6 +78,9 @@ static struct ipv6_l3fwd_lpm_route ipv6_l3fwd_lpm_route_array[] = {
 
 struct rte_lpm *ipv4_l3fwd_lpm_lookup_struct[NB_SOCKETS];
 struct rte_lpm6 *ipv6_l3fwd_lpm_lookup_struct[NB_SOCKETS];
+struct rte_rcu_qsbr *lpm4_qsv[NB_SOCKETS];	/* RCU QSBR variable for LPM4 */
+extern int numa_on;
+extern int rw_lf;
 
 static inline uint16_t
 lpm_get_ipv4_dst_port(void *ipv4_hdr, uint16_t portid, void *lookup_struct)
@@ -178,7 +182,7 @@ lpm_main_loop(__attribute__((unused)) void *dummy)
 	struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
 	unsigned lcore_id;
 	uint64_t prev_tsc, diff_tsc, cur_tsc;
-	int i, nb_rx;
+	int i, nb_rx, socketid = 0;
 	uint16_t portid;
 	uint8_t queueid;
 	struct lcore_conf *qconf;
@@ -206,6 +210,22 @@ lpm_main_loop(__attribute__((unused)) void *dummy)
 			lcore_id, portid, queueid);
 	}
 
+	if (rw_lf) {
+		if (numa_on)
+			socketid = rte_lcore_to_socket_id(lcore_id);
+		else
+			socketid = 0;
+
+		if (rte_rcu_qsbr_thread_register(lpm4_qsv[socketid],
+						lcore_id) != 0) {
+			RTE_LOG(ERR, L3FWD,
+				"lcore %u failed RCU QSBR register\n",
+				lcore_id);
+			return -1;
+		}
+		rte_rcu_qsbr_thread_online(lpm4_qsv[socketid], lcore_id);
+	}
+
 	while (!force_quit) {
 
 		cur_tsc = rte_rdtsc();
@@ -237,8 +257,12 @@ lpm_main_loop(__attribute__((unused)) void *dummy)
 			queueid = qconf->rx_queue_list[i].queue_id;
 			nb_rx = rte_eth_rx_burst(portid, queueid, pkts_burst,
 				MAX_PKT_BURST);
-			if (nb_rx == 0)
+			if (nb_rx == 0) {
+				if (rw_lf)
+					rte_rcu_qsbr_quiescent(
+						lpm4_qsv[socketid], lcore_id);
 				continue;
+			}
 
 #if defined RTE_ARCH_X86 || defined RTE_MACHINE_CPUFLAG_NEON \
 			 || defined RTE_ARCH_PPC_64
@@ -248,6 +272,20 @@ lpm_main_loop(__attribute__((unused)) void *dummy)
 			l3fwd_lpm_no_opt_send_packets(nb_rx, pkts_burst,
 							portid, qconf);
 #endif /* X86 */
+			if (rw_lf)
+				rte_rcu_qsbr_quiescent(lpm4_qsv[socketid],
+							lcore_id);
+		}
+	}
+
+	if (rw_lf) {
+		rte_rcu_qsbr_thread_offline(lpm4_qsv[socketid], lcore_id);
+		if (rte_rcu_qsbr_thread_unregister(lpm4_qsv[socketid],
+						lcore_id) != 0) {
+			RTE_LOG(ERR, L3FWD,
+				"lcore %u failed RCU QSBR unregister\n",
+				lcore_id);
+			return -1;
 		}
 	}
 
@@ -303,6 +341,34 @@ setup_lpm(const int socketid, __rte_unused const unsigned int flags)
 			ipv4_l3fwd_lpm_route_array[i].if_out);
 	}
 
+	if (rw_lf) {
+		size_t sz;
+
+		/* create RCU QSBR variable */
+		sz = rte_rcu_qsbr_get_memsize(RTE_MAX_LCORE);
+		lpm4_qsv[socketid] = (struct rte_rcu_qsbr *)rte_zmalloc_socket(
+						NULL, sz, RTE_CACHE_LINE_SIZE,
+						socketid);
+		if (lpm4_qsv[socketid] == NULL)
+			rte_exit(EXIT_FAILURE,
+				"RCU QSBR alloc fails on socket %d\n",
+				socketid);
+		else {
+			if (rte_rcu_qsbr_init(lpm4_qsv[socketid],
+						RTE_MAX_LCORE) != 0)
+				rte_exit(EXIT_FAILURE,
+					"RCU QSBR init fails on socket %d\n",
+					socketid);
+		}
+
+		/* attach RCU QSBR to LPM table */
+		if (rte_lpm_rcu_qsbr_add(ipv4_l3fwd_lpm_lookup_struct[socketid],
+					lpm4_qsv[socketid]) != 0)
+			rte_exit(EXIT_FAILURE,
+				"RCU QSBR attach to LPM fails on socket %d\n",
+				socketid);
+	}
+
 	/* create the LPM6 table */
 	snprintf(s, sizeof(s), "IPV6_L3FWD_LPM_%d", socketid);
 
diff --git a/examples/l3fwd/main.c b/examples/l3fwd/main.c
index 1b435e9eb..797b57554 100644
--- a/examples/l3fwd/main.c
+++ b/examples/l3fwd/main.c
@@ -72,12 +72,12 @@ static int l3fwd_em_on;
 
 /* Global variables. */
 
-static int numa_on = 1; /**< NUMA is enabled by default. */
+int numa_on = 1;	/**< NUMA is enabled by default. */
 static int parse_ptype; /**< Parse packet type using rx callback, and */
 			/**< disabled by default */
 static int per_port_pool; /**< Use separate buffer pools per port; disabled */
 			  /**< by default */
-static int rw_lf;	/**< Enable lock-free read-write concurrency, */
+int rw_lf;		/**< Enable lock-free read-write concurrency, */
 			/**< disabled by default */
 
 /* Global variables. */
@@ -619,6 +619,7 @@ parse_args(int argc, char **argv)
 			break;
 
 		case CMD_LINE_OPT_LOCK_FREE_NUM:
+			printf("RCU lock-free is enabled\n");
 			rw_lf = 1;
 			break;
 
diff --git a/examples/l3fwd/meson.build b/examples/l3fwd/meson.build
index 6dd4b9022..d6e462a1f 100644
--- a/examples/l3fwd/meson.build
+++ b/examples/l3fwd/meson.build
@@ -6,6 +6,7 @@
 # To build this example as a standalone application with an already-installed
 # DPDK instance, use 'make'
 
+allow_experimental_apis = true
 deps += ['hash', 'lpm']
 sources = files(
 	'l3fwd_em.c', 'l3fwd_lpm.c', 'main.c'
