diff --git a/example/l3fwd/odp_l3fwd.c b/example/l3fwd/odp_l3fwd.c
index 5f8eaa0..29da045 100644
--- a/example/l3fwd/odp_l3fwd.c
+++ b/example/l3fwd/odp_l3fwd.c
@@ -25,11 +25,25 @@ static const char * const route_str[] = {
 	"2.1.1.0/24:fm1-mac2:00.e0.0c.00.85.01",
 };
 
+#define MAX_NB_WORKER	8
+#define MAX_NB_PKTIO	4
+
+struct l3fwd_pktio_s {
+	odp_pktio_t pktio;
+	odp_pktin_queue_t ifin;
+	odp_pktout_queue_t ifout;
+};
+
+struct thread_arg_s {
+	uint32_t if_idx;
+};
+
 struct {
-	odp_pktio_t if0, if1;
-	odp_pktin_queue_t if0in, if1in;
-	odp_pktout_queue_t if0out, if1out;
-	odph_ethaddr_t src, dst;
+	struct l3fwd_pktio_s	l3fwd_pktios[MAX_NB_PKTIO];
+	odph_linux_pthread_t	l3fwd_workers[MAX_NB_WORKER];
+	struct thread_arg_s	worker_args[MAX_NB_WORKER];
+	uint32_t		nb_pktio;  /* effective pktios */
+	uint32_t		nb_worker; /* effective workers */
 } global;
 
 static odp_pktio_t create_pktio(const char *name, odp_pool_t pool,
@@ -77,30 +91,28 @@ static odp_pktio_t create_pktio(const char *name, odp_pool_t pool,
 	return pktio;
 }
 
-static void *run_worker(void *arg ODP_UNUSED)
+static void *run_worker(void *arg)
 {
 	odp_packet_t pkt_tbl[MAX_PKT_BURST];
 	odp_packet_t pkt_tbl_drop[MAX_PKT_BURST];
-	int pkts, i;
+	uint32_t pkts, i;
+	struct l3fwd_pktio_s *port;
 
-	if (odp_pktio_start(global.if0)) {
-		printf("unable to start input interface\n");
-		exit(1);
-	}
-	printf("started input interface\n");
-	if (odp_pktio_start(global.if1)) {
-		printf("unable to start output interface\n");
+	i = ((struct thread_arg_s *)arg)->if_idx;
+	port = &global.l3fwd_pktios[i];
+
+	if (odp_pktio_start(port->pktio)) {
+		printf("unable to start interface: %d\n", i);
 		exit(1);
 	}
-	printf("started output interface\n");
-	printf("started all\n");
 
+	printf("start interface: %d\n", i);
 	for (;;) {
 		int need_to_drop = 0;
 
 		memset(pkt_tbl, 0, sizeof(pkt_tbl_drop));
 
-		pkts = odp_pktin_recv(global.if0in, pkt_tbl, MAX_PKT_BURST);
+		pkts = odp_pktin_recv(port->ifin, pkt_tbl, MAX_PKT_BURST);
 		if (odp_unlikely(pkts <= 0))
 			continue;
 		for (i = 0; i < pkts; i++) {
@@ -164,10 +176,9 @@ int main(int argc, char **argv)
 	odp_pool_t pool;
 	odp_pool_param_t params;
 	odp_cpumask_t cpumask;
-	odph_linux_pthread_t thd;
 	odp_instance_t instance;
 	odph_linux_thr_params_t thr_params;
-	size_t i;
+	uint32_t cpu, i;
 	uint8_t mac[ODPH_ETHADDR_LEN];
 
 	if (argc != 3) {
@@ -187,6 +198,9 @@ int main(int argc, char **argv)
 		exit(1);
 	}
 
+	/* Clear global argument */
+	memset(&global, 0, sizeof(global));
+
 	/* Init l3fwd tale */
 	init_fwd_db();
 
@@ -212,24 +226,41 @@ int main(int argc, char **argv)
 		exit(1);
 	}
 
-	global.if0 = create_pktio(argv[1], pool, &global.if0in, &global.if0out);
-	global.if1 = create_pktio(argv[2], pool, &global.if1in, &global.if1out);
-
-	/* resolve route table */
-	odp_pktio_mac_addr(global.if0, mac, ODPH_ETHADDR_LEN);
-	resolve_fwd_db(argv[1], global.if0, mac);
-	odp_pktio_mac_addr(global.if1, mac, ODPH_ETHADDR_LEN);
-	resolve_fwd_db(argv[2], global.if1, mac);
-
-	odp_cpumask_default_worker(&cpumask, 1);
+	/* TODO: parse cmdline to get pktio number, name */
+	global.nb_pktio = 2;
+	for (i = 0; i < global.nb_pktio; i++) {
+		struct l3fwd_pktio_s *port;
+		char *ifname = argv[i + 1];
+
+		port = &global.l3fwd_pktios[i];
+		port->pktio = create_pktio(ifname, pool, &port->ifin,
+					   &port->ifout);
+		odp_pktio_mac_addr(port->pktio, mac, ODPH_ETHADDR_LEN);
+		resolve_fwd_db(ifname, port->pktio, mac);
+	}
 
+	global.nb_worker = 2;
 	memset(&thr_params, 0, sizeof(thr_params));
 	thr_params.start    = run_worker;
-	thr_params.arg      = NULL;
 	thr_params.thr_type = ODP_THREAD_WORKER;
 	thr_params.instance = instance;
 
-	odph_linux_pthread_create(&thd, &cpumask, &thr_params);
-	odph_linux_pthread_join(&thd, 1);
+	odp_cpumask_default_worker(&cpumask, global.nb_worker);
+	cpu = odp_cpumask_first(&cpumask);
+	for (i = 0; i < global.nb_worker; i++) {
+		struct thread_arg_s *arg;
+		odp_cpumask_t thr_mask;
+
+		odp_cpumask_zero(&thr_mask);
+		odp_cpumask_set(&thr_mask, cpu);
+		arg = &global.worker_args[i];
+		arg->if_idx = i;
+		thr_params.arg      = arg;
+		odph_linux_pthread_create(&global.l3fwd_workers[i], &thr_mask,
+					  &thr_params);
+		cpu = odp_cpumask_next(&cpumask, cpu);
+	}
+	odph_linux_pthread_join(&global.l3fwd_workers[0], global.nb_worker);
+
 	return 0;
 }
