diff mbox

Added process mode to example app

Message ID 1412252681-17915-1-git-send-email-petri.savolainen@linaro.org
State Accepted
Commit d988593a73cd2882b6ddfa5434e798f89e4b42e0
Headers show

Commit Message

Petri Savolainen Oct. 2, 2014, 12:24 p.m. UTC
- Added an option to run odp_example as Linux processes (vs. pthreads)
- Removed thread dependency from odp_local_init
- Added Linux helpers for forking processes
- modified odp_thread.c to allocate global variables from shared memory

Signed-off-by: Petri Savolainen <petri.savolainen@linaro.org>
---
 example/generator/odp_generator.c               |  10 +-
 example/ipsec/odp_ipsec.c                       |   7 +-
 example/l2fwd/odp_l2fwd.c                       |  11 +-
 example/odp_example/odp_example.c               | 184 ++++++++++++++++--------
 example/packet/odp_pktio.c                      |  11 +-
 example/timer/odp_timer_test.c                  |  14 +-
 helper/include/odph_linux.h                     |  66 ++++++++-
 platform/linux-generic/include/api/odp_init.h   |   4 +-
 platform/linux-generic/include/api/odp_thread.h |  17 +--
 platform/linux-generic/include/odp_internal.h   |   4 +-
 platform/linux-generic/odp_init.c               |  14 +-
 platform/linux-generic/odp_linux.c              | 113 +++++++++++++--
 platform/linux-generic/odp_thread.c             |  84 ++++++++---
 13 files changed, 401 insertions(+), 138 deletions(-)

Comments

Maxim Uvarov Oct. 3, 2014, 12:44 p.m. UTC | #1
Thanks Petri, I did some tests with it and it works :) So that it's 
great that now we can scale for number of processes as well as for 
number of threads.

Maxim.

On 10/02/2014 04:24 PM, Petri Savolainen wrote:
> - Added an option to run odp_example as Linux processes (vs. pthreads)
> - Removed thread dependency from odp_local_init
> - Added Linux helpers for forking processes
> - modified odp_thread.c to allocate global variables from shared memory
>
> Signed-off-by: Petri Savolainen <petri.savolainen@linaro.org>
> ---
>   example/generator/odp_generator.c               |  10 +-
>   example/ipsec/odp_ipsec.c                       |   7 +-
>   example/l2fwd/odp_l2fwd.c                       |  11 +-
>   example/odp_example/odp_example.c               | 184 ++++++++++++++++--------
>   example/packet/odp_pktio.c                      |  11 +-
>   example/timer/odp_timer_test.c                  |  14 +-
>   helper/include/odph_linux.h                     |  66 ++++++++-
>   platform/linux-generic/include/api/odp_init.h   |   4 +-
>   platform/linux-generic/include/api/odp_thread.h |  17 +--
>   platform/linux-generic/include/odp_internal.h   |   4 +-
>   platform/linux-generic/odp_init.c               |  14 +-
>   platform/linux-generic/odp_linux.c              | 113 +++++++++++++--
>   platform/linux-generic/odp_thread.c             |  84 ++++++++---
>   13 files changed, 401 insertions(+), 138 deletions(-)
>
> diff --git a/example/generator/odp_generator.c b/example/generator/odp_generator.c
> index 78d9df5..6055324 100644
> --- a/example/generator/odp_generator.c
> +++ b/example/generator/odp_generator.c
> @@ -511,7 +511,6 @@ int main(int argc, char *argv[])
>   {
>   	odph_linux_pthread_t thread_tbl[MAX_WORKERS];
>   	odp_buffer_pool_t pool;
> -	int thr_id;
>   	int num_workers;
>   	void *pool_base;
>   	int i;
> @@ -525,6 +524,11 @@ int main(int argc, char *argv[])
>   		exit(EXIT_FAILURE);
>   	}
>   
> +	if (odp_init_local()) {
> +		ODP_ERR("Error: ODP local init failed.\n");
> +		exit(EXIT_FAILURE);
> +	}
> +
>   	/* init counters */
>   	odp_atomic_init_u64(&counters.seq);
>   	odp_atomic_init_u64(&counters.ip);
> @@ -574,10 +578,6 @@ int main(int argc, char *argv[])
>   
>   	printf("First core:         %i\n\n", first_core);
>   
> -	/* Init this thread */
> -	thr_id = odp_thread_create(0);
> -	odp_init_local(thr_id);
> -
>   	/* Create packet pool */
>   	shm = odp_shm_reserve("shm_packet_pool",
>   			      SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE, 0);
> diff --git a/example/ipsec/odp_ipsec.c b/example/ipsec/odp_ipsec.c
> index cd94d9a..fa4100f 100644
> --- a/example/ipsec/odp_ipsec.c
> +++ b/example/ipsec/odp_ipsec.c
> @@ -1166,7 +1166,6 @@ int
>   main(int argc, char *argv[])
>   {
>   	odph_linux_pthread_t thread_tbl[MAX_WORKERS];
> -	int thr_id;
>   	int num_workers;
>   	void *pool_base;
>   	int i;
> @@ -1182,8 +1181,10 @@ main(int argc, char *argv[])
>   	}
>   
>   	/* Init this thread */
> -	thr_id = odp_thread_create(0);
> -	odp_init_local(thr_id);
> +	if (odp_init_local()) {
> +		ODP_ERR("Error: ODP local init failed.\n");
> +		exit(EXIT_FAILURE);
> +	}
>   
>   	/* Reserve memory for args from shared mem */
>   	shm = odp_shm_reserve("shm_args", sizeof(args_t), ODP_CACHE_LINE_SIZE,
> diff --git a/example/l2fwd/odp_l2fwd.c b/example/l2fwd/odp_l2fwd.c
> index fb325c4..8aa0ba0 100644
> --- a/example/l2fwd/odp_l2fwd.c
> +++ b/example/l2fwd/odp_l2fwd.c
> @@ -311,7 +311,6 @@ int main(int argc, char *argv[])
>   {
>   	odph_linux_pthread_t thread_tbl[MAX_WORKERS];
>   	odp_buffer_pool_t pool;
> -	int thr_id;
>   	void *pool_base;
>   	int i;
>   	int first_core;
> @@ -325,6 +324,12 @@ int main(int argc, char *argv[])
>   		exit(EXIT_FAILURE);
>   	}
>   
> +	/* Init this thread */
> +	if (odp_init_local()) {
> +		ODP_ERR("Error: ODP local init failed.\n");
> +		exit(EXIT_FAILURE);
> +	}
> +
>   	/* Reserve memory for args from shared mem */
>   	shm = odp_shm_reserve("shm_args", sizeof(args_t),
>   			      ODP_CACHE_LINE_SIZE, 0);
> @@ -374,10 +379,6 @@ int main(int argc, char *argv[])
>   
>   	printf("First core:         %i\n\n", first_core);
>   
> -	/* Init this thread */
> -	thr_id = odp_thread_create(0);
> -	odp_init_local(thr_id);
> -
>   	/* Create packet pool */
>   	shm = odp_shm_reserve("shm_packet_pool",
>   			      SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE, 0);
> diff --git a/example/odp_example/odp_example.c b/example/odp_example/odp_example.c
> index c80dbbc..47d764e 100644
> --- a/example/odp_example/odp_example.c
> +++ b/example/odp_example/odp_example.c
> @@ -46,12 +46,15 @@ typedef struct {
>   
>   /** Test arguments */
>   typedef struct {
> -	int core_count; /**< Core count*/
> +	int core_count; /**< Core count */
> +	int proc_mode;  /**< Process mode */
>   } test_args_t;
>   
>   
> -/** @private Barrier for test synchronisation */
> -static odp_barrier_t test_barrier;
> +/** Test global variables */
> +typedef struct {
> +	odp_barrier_t barrier;/**< @private Barrier for test synchronisation */
> +} test_globals_t;
>   
>   
>   /**
> @@ -318,7 +321,8 @@ static int test_poll_queue(int thr, odp_buffer_pool_t msg_pool)
>    * @return 0 if successful
>    */
>   static int test_schedule_one_single(const char *str, int thr,
> -				    odp_buffer_pool_t msg_pool, int prio)
> +				    odp_buffer_pool_t msg_pool,
> +				    int prio, odp_barrier_t *barrier)
>   {
>   	odp_buffer_t buf;
>   	odp_queue_t queue;
> @@ -348,7 +352,7 @@ static int test_schedule_one_single(const char *str, int thr,
>   	ns     = odp_time_cycles_to_ns(cycles);
>   	tot    = i;
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   	clear_sched_queues();
>   
>   	if (tot) {
> @@ -379,7 +383,8 @@ static int test_schedule_one_single(const char *str, int thr,
>    * @return 0 if successful
>    */
>   static int test_schedule_one_many(const char *str, int thr,
> -				  odp_buffer_pool_t msg_pool, int prio)
> +				  odp_buffer_pool_t msg_pool,
> +				  int prio, odp_barrier_t *barrier)
>   {
>   	odp_buffer_t buf;
>   	odp_queue_t queue;
> @@ -412,7 +417,7 @@ static int test_schedule_one_many(const char *str, int thr,
>   	ns     = odp_time_cycles_to_ns(cycles);
>   	tot    = i;
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   	clear_sched_queues();
>   
>   	if (tot) {
> @@ -443,7 +448,8 @@ static int test_schedule_one_many(const char *str, int thr,
>    * @return 0 if successful
>    */
>   static int test_schedule_single(const char *str, int thr,
> -				odp_buffer_pool_t msg_pool, int prio)
> +				odp_buffer_pool_t msg_pool,
> +				int prio, odp_barrier_t *barrier)
>   {
>   	odp_buffer_t buf;
>   	odp_queue_t queue;
> @@ -490,7 +496,7 @@ static int test_schedule_single(const char *str, int thr,
>   	cycles = odp_time_diff_cycles(t1, t2);
>   	ns     = odp_time_cycles_to_ns(cycles);
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   	clear_sched_queues();
>   
>   	if (tot) {
> @@ -522,7 +528,8 @@ static int test_schedule_single(const char *str, int thr,
>    * @return 0 if successful
>    */
>   static int test_schedule_many(const char *str, int thr,
> -			      odp_buffer_pool_t msg_pool, int prio)
> +			      odp_buffer_pool_t msg_pool,
> +			      int prio, odp_barrier_t *barrier)
>   {
>   	odp_buffer_t buf;
>   	odp_queue_t queue;
> @@ -572,7 +579,7 @@ static int test_schedule_many(const char *str, int thr,
>   	cycles = odp_time_diff_cycles(t1, t2);
>   	ns     = odp_time_cycles_to_ns(cycles);
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   	clear_sched_queues();
>   
>   	if (tot) {
> @@ -600,7 +607,8 @@ static int test_schedule_many(const char *str, int thr,
>    * @return 0 if successful
>    */
>   static int test_schedule_multi(const char *str, int thr,
> -			       odp_buffer_pool_t msg_pool, int prio)
> +			       odp_buffer_pool_t msg_pool,
> +			       int prio, odp_barrier_t *barrier)
>   {
>   	odp_buffer_t buf[MULTI_BUFS_MAX];
>   	odp_queue_t queue;
> @@ -682,7 +690,7 @@ static int test_schedule_multi(const char *str, int thr,
>   	cycles = odp_time_diff_cycles(t1, t2);
>   	ns     = odp_time_cycles_to_ns(cycles);
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   	clear_sched_queues();
>   
>   	if (tot) {
> @@ -710,18 +718,31 @@ static void *run_thread(void *arg)
>   {
>   	int thr;
>   	odp_buffer_pool_t msg_pool;
> +	odp_shm_t shm;
> +	test_globals_t *globals;
> +	odp_barrier_t *barrier;
>   
>   	thr = odp_thread_id();
>   
>   	printf("Thread %i starts on core %i\n", thr, odp_thread_core());
>   
> +	shm     = odp_shm_lookup("test_globals");
> +	globals = odp_shm_addr(shm);
> +
> +	if (globals == NULL) {
> +		ODP_ERR("Shared mem lookup failed\n");
> +		return NULL;
> +	}
> +
> +	barrier = &globals->barrier;
> +
>   	/*
>   	 * Test barriers back-to-back
>   	 */
> -	odp_barrier_sync(&test_barrier);
> -	odp_barrier_sync(&test_barrier);
> -	odp_barrier_sync(&test_barrier);
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
> +	odp_barrier_sync(barrier);
> +	odp_barrier_sync(barrier);
> +	odp_barrier_sync(barrier);
>   
>   	/*
>   	 * Find the buffer pool
> @@ -733,83 +754,83 @@ static void *run_thread(void *arg)
>   		return NULL;
>   	}
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   
>   	if (test_alloc_single(thr, msg_pool))
>   		return NULL;
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   
>   	if (test_alloc_multi(thr, msg_pool))
>   		return NULL;
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   
>   	if (test_poll_queue(thr, msg_pool))
>   		return NULL;
>   
>   	/* Low prio */
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   
>   	if (test_schedule_one_single("sched_one_s_lo", thr, msg_pool,
> -				     ODP_SCHED_PRIO_LOWEST))
> +				     ODP_SCHED_PRIO_LOWEST, barrier))
>   		return NULL;
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   
>   	if (test_schedule_single("sched_____s_lo", thr, msg_pool,
> -				 ODP_SCHED_PRIO_LOWEST))
> +				 ODP_SCHED_PRIO_LOWEST, barrier))
>   		return NULL;
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   
>   	if (test_schedule_one_many("sched_one_m_lo", thr, msg_pool,
> -				   ODP_SCHED_PRIO_LOWEST))
> +				   ODP_SCHED_PRIO_LOWEST, barrier))
>   		return NULL;
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   
>   	if (test_schedule_many("sched_____m_lo", thr, msg_pool,
> -			       ODP_SCHED_PRIO_LOWEST))
> +			       ODP_SCHED_PRIO_LOWEST, barrier))
>   		return NULL;
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   
>   	if (test_schedule_multi("sched_multi_lo", thr, msg_pool,
> -				ODP_SCHED_PRIO_LOWEST))
> +				ODP_SCHED_PRIO_LOWEST, barrier))
>   		return NULL;
>   
>   	/* High prio */
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   
>   	if (test_schedule_one_single("sched_one_s_hi", thr, msg_pool,
> -				     ODP_SCHED_PRIO_HIGHEST))
> +				     ODP_SCHED_PRIO_HIGHEST, barrier))
>   		return NULL;
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   
>   	if (test_schedule_single("sched_____s_hi", thr, msg_pool,
> -				 ODP_SCHED_PRIO_HIGHEST))
> +				 ODP_SCHED_PRIO_HIGHEST, barrier))
>   		return NULL;
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   
>   	if (test_schedule_one_many("sched_one_m_hi", thr, msg_pool,
> -				   ODP_SCHED_PRIO_HIGHEST))
> +				   ODP_SCHED_PRIO_HIGHEST, barrier))
>   		return NULL;
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   
>   	if (test_schedule_many("sched_____m_hi", thr, msg_pool,
> -			       ODP_SCHED_PRIO_HIGHEST))
> +			       ODP_SCHED_PRIO_HIGHEST, barrier))
>   		return NULL;
>   
> -	odp_barrier_sync(&test_barrier);
> +	odp_barrier_sync(barrier);
>   
>   	if (test_schedule_multi("sched_multi_hi", thr, msg_pool,
> -				ODP_SCHED_PRIO_HIGHEST))
> +				ODP_SCHED_PRIO_HIGHEST, barrier))
>   		return NULL;
>   
>   
> @@ -884,6 +905,7 @@ static void print_usage(void)
>   	printf("Options:\n");
>   	printf("  -c, --count <number>    core count, core IDs start from 1\n");
>   	printf("  -h, --help              this help\n");
> +	printf("  --proc                  process mode\n");
>   	printf("\n\n");
>   }
>   
> @@ -902,6 +924,7 @@ static void parse_args(int argc, char *argv[], test_args_t *args)
>   	static struct option longopts[] = {
>   		{"count", required_argument, NULL, 'c'},
>   		{"help", no_argument, NULL, 'h'},
> +		{"proc", no_argument, NULL, 0},
>   		{NULL, 0, NULL, 0}
>   	};
>   
> @@ -912,6 +935,10 @@ static void parse_args(int argc, char *argv[], test_args_t *args)
>   			break;	/* No more options */
>   
>   		switch (opt) {
> +		case 0:
> +			args->proc_mode = 1;
> +			break;
> +
>   		case 'c':
>   			args->core_count = atoi(optarg);
>   			break;
> @@ -935,7 +962,6 @@ int main(int argc, char *argv[])
>   {
>   	odph_linux_pthread_t thread_tbl[MAX_WORKERS];
>   	test_args_t args;
> -	int thr_id;
>   	int num_workers;
>   	odp_buffer_pool_t pool;
>   	void *pool_base;
> @@ -944,16 +970,32 @@ int main(int argc, char *argv[])
>   	int prios;
>   	int first_core;
>   	odp_shm_t shm;
> +	test_globals_t *globals;
>   
> -	printf("\nODP example starts\n");
> +	printf("\nODP example starts\n\n");
>   
>   	memset(&args, 0, sizeof(args));
>   	parse_args(argc, argv, &args);
>   
> +	if (args.proc_mode)
> +		printf("Process mode\n");
> +	else
> +		printf("Thread mode\n");
> +
>   	memset(thread_tbl, 0, sizeof(thread_tbl));
>   
> +	/* ODP global init */
>   	if (odp_init_global()) {
> -		printf("ODP global init failed.\n");
> +		ODP_ERR("ODP global init failed.\n");
> +		return -1;
> +	}
> +
> +	/*
> +	 * Init this thread. It makes also ODP calls when
> +	 * setting up resources for worker threads.
> +	 */
> +	if (odp_init_local()) {
> +		ODP_ERR("ODP global init failed.\n");
>   		return -1;
>   	}
>   
> @@ -991,16 +1033,22 @@ int main(int argc, char *argv[])
>   
>   	printf("first core:         %i\n", first_core);
>   
> -	/*
> -	 * Init this thread. It makes also ODP calls when
> -	 * setting up resources for worker threads.
> -	 */
> -	thr_id = odp_thread_create(0);
> -	odp_init_local(thr_id);
>   
>   	/* Test cycle count accuracy */
>   	test_time();
>   
> +	shm = odp_shm_reserve("test_globals",
> +			      sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0);
> +
> +	globals = odp_shm_addr(shm);
> +
> +	if (globals == NULL) {
> +		ODP_ERR("Shared memory reserve failed.\n");
> +		return -1;
> +	}
> +
> +	memset(globals, 0, sizeof(test_globals_t));
> +
>   	/*
>   	 * Create message pool
>   	 */
> @@ -1072,16 +1120,40 @@ int main(int argc, char *argv[])
>   	odp_shm_print_all();
>   
>   	/* Barrier to sync test case execution */
> -	odp_barrier_init_count(&test_barrier, num_workers);
> +	odp_barrier_init_count(&globals->barrier, num_workers);
>   
> -	/* Create and launch worker threads */
> -	odph_linux_pthread_create(thread_tbl, num_workers, first_core,
> -				  run_thread, NULL);
> +	if (args.proc_mode) {
> +		int ret;
> +		odph_linux_process_t proc[MAX_WORKERS];
>   
> -	/* Wait for worker threads to exit */
> -	odph_linux_pthread_join(thread_tbl, num_workers);
> +		/* Fork worker processes */
> +		ret = odph_linux_process_fork_n(proc, num_workers,
> +						first_core);
>   
> -	printf("ODP example complete\n\n");
> +		if (ret < 0) {
> +			ODP_ERR("Fork workers failed %i\n", ret);
> +			return -1;
> +		}
> +
> +		if (ret == 0) {
> +			/* Child process */
> +			run_thread(NULL);
> +		} else {
> +			/* Parent process */
> +			odph_linux_process_wait_n(proc, num_workers);
> +			printf("ODP example complete\n\n");
> +		}
> +
> +	} else {
> +		/* Create and launch worker threads */
> +		odph_linux_pthread_create(thread_tbl, num_workers, first_core,
> +					  run_thread, NULL);
> +
> +		/* Wait for worker threads to terminate */
> +		odph_linux_pthread_join(thread_tbl, num_workers);
> +
> +		printf("ODP example complete\n\n");
> +	}
>   
>   	return 0;
>   }
> diff --git a/example/packet/odp_pktio.c b/example/packet/odp_pktio.c
> index a949a05..145ae47 100644
> --- a/example/packet/odp_pktio.c
> +++ b/example/packet/odp_pktio.c
> @@ -291,7 +291,6 @@ int main(int argc, char *argv[])
>   {
>   	odph_linux_pthread_t thread_tbl[MAX_WORKERS];
>   	odp_buffer_pool_t pool;
> -	int thr_id;
>   	int num_workers;
>   	void *pool_base;
>   	int i;
> @@ -305,6 +304,12 @@ int main(int argc, char *argv[])
>   		exit(EXIT_FAILURE);
>   	}
>   
> +	/* Init this thread */
> +	if (odp_init_local()) {
> +		ODP_ERR("Error: ODP local init failed.\n");
> +		exit(EXIT_FAILURE);
> +	}
> +
>   	/* Reserve memory for args from shared mem */
>   	shm = odp_shm_reserve("shm_args", sizeof(args_t),
>   			      ODP_CACHE_LINE_SIZE, 0);
> @@ -344,10 +349,6 @@ int main(int argc, char *argv[])
>   
>   	printf("First core:         %i\n\n", first_core);
>   
> -	/* Init this thread */
> -	thr_id = odp_thread_create(0);
> -	odp_init_local(thr_id);
> -
>   	/* Create packet pool */
>   	shm = odp_shm_reserve("shm_packet_pool",
>   			      SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE, 0);
> diff --git a/example/timer/odp_timer_test.c b/example/timer/odp_timer_test.c
> index 6e1715d..87900fc 100644
> --- a/example/timer/odp_timer_test.c
> +++ b/example/timer/odp_timer_test.c
> @@ -240,7 +240,6 @@ int main(int argc, char *argv[])
>   {
>   	odph_linux_pthread_t thread_tbl[MAX_WORKERS];
>   	test_args_t args;
> -	int thr_id;
>   	int num_workers;
>   	odp_buffer_pool_t pool;
>   	void *pool_base;
> @@ -262,6 +261,12 @@ int main(int argc, char *argv[])
>   		return -1;
>   	}
>   
> +	/* Init this thread. */
> +	if (odp_init_local()) {
> +		printf("ODP local init failed.\n");
> +		return -1;
> +	}
> +
>   	printf("\n");
>   	printf("ODP system info\n");
>   	printf("---------------\n");
> @@ -302,13 +307,6 @@ int main(int argc, char *argv[])
>   	printf("timeouts:           %i\n", args.tmo_count);
>   
>   	/*
> -	 * Init this thread. It makes also ODP calls when
> -	 * setting up resources for worker threads.
> -	 */
> -	thr_id = odp_thread_create(0);
> -	odp_init_local(thr_id);
> -
> -	/*
>   	 * Create message pool
>   	 */
>   	shm = odp_shm_reserve("msg_pool",
> diff --git a/helper/include/odph_linux.h b/helper/include/odph_linux.h
> index 1ea349a..8671dc0 100644
> --- a/helper/include/odph_linux.h
> +++ b/helper/include/odph_linux.h
> @@ -10,8 +10,9 @@
>    *
>    * ODP Linux helper API
>    *
> - * This file is an optional helper to odp.h APIs. Application can manage
> - * pthreads also by other means.
> + * This file is an optional helper to odp.h APIs. These functions are provided
> + * to ease common setups in a Linux system. User is free to implement the same
> + * setups in otherways (not via this API).
>    */
>   
>   #ifndef ODP_LINUX_H_
> @@ -23,15 +24,24 @@ extern "C" {
>   
>   
>   #include <pthread.h>
> +#include <sys/types.h>
>   
> -/** Pthread status data */
> +/** Linux pthread state information */
>   typedef struct {
> -	pthread_t      thread; /**< @private Pthread */
> -	pthread_attr_t attr;   /**< @private Pthread attributes */
> -
> +	pthread_t      thread; /**< Pthread ID */
> +	pthread_attr_t attr;   /**< Pthread attributes */
> +	int            core;   /**< Core ID */
>   } odph_linux_pthread_t;
>   
>   
> +/** Linux process state information */
> +typedef struct {
> +	pid_t pid;      /**< Process ID */
> +	int   core;     /**< Core ID */
> +	int   status;   /**< Process state change status */
> +} odph_linux_process_t;
> +
> +
>   /**
>    * Creates and launches pthreads
>    *
> @@ -61,6 +71,50 @@ void odph_linux_pthread_create(odph_linux_pthread_t *thread_tbl,
>   void odph_linux_pthread_join(odph_linux_pthread_t *thread_tbl, int num);
>   
>   
> +/**
> + * Fork a process
> + *
> + * Forks and sets core affinity for the child process
> + *
> + * @param proc          Pointer to process state info (for output)
> + * @param core          Destination core for the child process
> + *
> + * @return On success: 1 for the parent, 0 for the child
> + *         On failure: -1 for the parent, -2 for the child
> + */
> +int odph_linux_process_fork(odph_linux_process_t *proc, int core);
> +
> +
> +/**
> + * Fork a number of processes
> + *
> + * Forks and sets core affinity for child processes
> + *
> + * @param proc_tbl      Process state info table (for output)
> + * @param num           Number of processes to create
> + * @param first_core    Destination core for the first process
> + *
> + * @return On success: 1 for the parent, 0 for the child
> + *         On failure: -1 for the parent, -2 for the child
> + */
> +int odph_linux_process_fork_n(odph_linux_process_t *proc_tbl,
> +			      int num, int first_core);
> +
> +
> +/**
> + * Wait for a number of processes
> + *
> + * Waits for a number of child processes to terminate. Records process state
> + * change status into the process state info structure.
> + *
> + * @param proc_tbl      Process state info table (previously filled by fork)
> + * @param num           Number of processes to wait
> + *
> + * @return 0 on success, -1 on failure
> + */
> +int odph_linux_process_wait_n(odph_linux_process_t *proc_tbl, int num);
> +
> +
>   #ifdef __cplusplus
>   }
>   #endif
> diff --git a/platform/linux-generic/include/api/odp_init.h b/platform/linux-generic/include/api/odp_init.h
> index 490324a..13c8e44 100644
> --- a/platform/linux-generic/include/api/odp_init.h
> +++ b/platform/linux-generic/include/api/odp_init.h
> @@ -41,10 +41,10 @@ int odp_init_global(void);
>    *
>    * All threads must call this function before calling
>    * any other ODP API functions.
> - * @param thr_id Thread id
> + *
>    * @return 0 if successful
>    */
> -int odp_init_local(int thr_id);
> +int odp_init_local(void);
>   
>   
>   
> diff --git a/platform/linux-generic/include/api/odp_thread.h b/platform/linux-generic/include/api/odp_thread.h
> index e8e8c8a..5567748 100644
> --- a/platform/linux-generic/include/api/odp_thread.h
> +++ b/platform/linux-generic/include/api/odp_thread.h
> @@ -8,7 +8,7 @@
>   /**
>    * @file
>    *
> - * ODP Linux helper API
> + * ODP thread API
>    */
>   
>   #ifndef ODP_THREAD_H_
> @@ -19,19 +19,6 @@ extern "C" {
>   #endif
>   
>   
> -
> -#include <odp_std_types.h>
> -
> -
> -
> -/**
> - * Create thread id
> - *
> - * @param  core Core dedicated for the thread
> - * @return New thread id
> - */
> -int odp_thread_create(int core);
> -
>   /**
>    * Get thread id
>    *
> @@ -41,7 +28,7 @@ int odp_thread_id(void);
>   
>   
>   /**
> - * Get thread id
> + * Get core id
>    *
>    * @return Core id where the thread is running currently
>    */
> diff --git a/platform/linux-generic/include/odp_internal.h b/platform/linux-generic/include/odp_internal.h
> index aa79493..f8c1596 100644
> --- a/platform/linux-generic/include/odp_internal.h
> +++ b/platform/linux-generic/include/odp_internal.h
> @@ -21,8 +21,8 @@ extern "C" {
>   
>   int odp_system_info_init(void);
>   
> -void odp_thread_init_global(void);
> -void odp_thread_init_local(int thr_id);
> +int odp_thread_init_global(void);
> +int odp_thread_init_local(void);
>   
>   int odp_shm_init_global(void);
>   int odp_shm_init_local(void);
> diff --git a/platform/linux-generic/odp_init.c b/platform/linux-generic/odp_init.c
> index 5b7e192..55fa53a 100644
> --- a/platform/linux-generic/odp_init.c
> +++ b/platform/linux-generic/odp_init.c
> @@ -11,8 +11,6 @@
>   
>   int odp_init_global(void)
>   {
> -	odp_thread_init_global();
> -
>   	odp_system_info_init();
>   
>   	if (odp_shm_init_global()) {
> @@ -20,6 +18,11 @@ int odp_init_global(void)
>   		return -1;
>   	}
>   
> +	if (odp_thread_init_global()) {
> +		ODP_ERR("ODP thread init failed.\n");
> +		return -1;
> +	}
> +
>   	if (odp_buffer_pool_init_global()) {
>   		ODP_ERR("ODP buffer pool init failed.\n");
>   		return -1;
> @@ -54,9 +57,12 @@ int odp_init_global(void)
>   }
>   
>   
> -int odp_init_local(int thr_id)
> +int odp_init_local(void)
>   {
> -	odp_thread_init_local(thr_id);
> +	if (odp_thread_init_local()) {
> +		ODP_ERR("ODP thread local init failed.\n");
> +		return -1;
> +	}
>   
>   	if (odp_pktio_init_local()) {
>   		ODP_ERR("ODP packet io local init failed.\n");
> diff --git a/platform/linux-generic/odp_linux.c b/platform/linux-generic/odp_linux.c
> index 9251ec9..cba6637 100644
> --- a/platform/linux-generic/odp_linux.c
> +++ b/platform/linux-generic/odp_linux.c
> @@ -8,12 +8,16 @@
>   #define _GNU_SOURCE
>   #endif
>   #include <sched.h>
> +#include <unistd.h>
> +#include <sys/types.h>
> +#include <sys/wait.h>
>   
>   #include <stdlib.h>
>   #include <string.h>
>   #include <stdio.h>
>   #include <assert.h>
>   
> +
>   #include <odph_linux.h>
>   #include <odp_internal.h>
>   #include <odp_thread.h>
> @@ -23,7 +27,6 @@
>   
>   
>   typedef struct {
> -	int thr_id;
>   	void *(*start_routine) (void *);
>   	void *arg;
>   
> @@ -35,9 +38,8 @@ static void *odp_run_start_routine(void *arg)
>   	odp_start_args_t *start_args = arg;
>   
>   	/* ODP thread local init */
> -	if (odp_init_local(start_args->thr_id)) {
> -		ODP_ERR("Local init failed for thread: %d\n",
> -			start_args->thr_id);
> +	if (odp_init_local()) {
> +		ODP_ERR("Local init failed\n");
>   		return NULL;
>   	}
>   
> @@ -65,9 +67,9 @@ void odph_linux_pthread_create(odph_linux_pthread_t *thread_tbl, int num,
>   	for (i = 0; i < num; i++) {
>   		pthread_attr_init(&thread_tbl[i].attr);
>   
> -		CPU_ZERO(&cpu_set);
> -
>   		cpu = (first_core + i) % core_count;
> +		thread_tbl[i].core = cpu;
> +		CPU_ZERO(&cpu_set);
>   		CPU_SET(cpu, &cpu_set);
>   
>   		pthread_attr_setaffinity_np(&thread_tbl[i].attr,
> @@ -81,8 +83,6 @@ void odph_linux_pthread_create(odph_linux_pthread_t *thread_tbl, int num,
>   		start_args->start_routine = start_routine;
>   		start_args->arg           = arg;
>   
> -		start_args->thr_id        = odp_thread_create(cpu);
> -
>   		pthread_create(&thread_tbl[i].thread, &thread_tbl[i].attr,
>   			       odp_run_start_routine, start_args);
>   	}
> @@ -98,3 +98,100 @@ void odph_linux_pthread_join(odph_linux_pthread_t *thread_tbl, int num)
>   		pthread_join(thread_tbl[i].thread, NULL);
>   	}
>   }
> +
> +
> +int odph_linux_process_fork_n(odph_linux_process_t *proc_tbl,
> +			      int num, int first_core)
> +{
> +	cpu_set_t cpu_set;
> +	pid_t pid;
> +	int core_count;
> +	int cpu;
> +	int i;
> +
> +	memset(proc_tbl, 0, num*sizeof(odph_linux_process_t));
> +
> +	core_count = odp_sys_core_count();
> +
> +	if (first_core < 0 || first_core >= core_count) {
> +		ODP_ERR("Bad first_core\n");
> +		return -1;
> +	}
> +
> +	if (num < 0 || num > core_count) {
> +		ODP_ERR("Bad num\n");
> +		return -1;
> +	}
> +
> +	for (i = 0; i < num; i++) {
> +		cpu = (first_core + i) % core_count;
> +		pid = fork();
> +
> +		if (pid < 0) {
> +			ODP_ERR("fork() failed\n");
> +			return -1;
> +		}
> +
> +		/* Parent continues to fork */
> +		if (pid > 0) {
> +			proc_tbl[i].pid  = pid;
> +			proc_tbl[i].core = cpu;
> +			continue;
> +		}
> +
> +		/* Child process */
> +		CPU_ZERO(&cpu_set);
> +		CPU_SET(cpu, &cpu_set);
> +
> +		if (sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set)) {
> +			ODP_ERR("sched_setaffinity() failed\n");
> +			return -2;
> +		}
> +
> +		if (odp_init_local()) {
> +			ODP_ERR("Local init failed\n");
> +			return -2;
> +		}
> +
> +		return 0;
> +	}
> +
> +	return 1;
> +}
> +
> +
> +int odph_linux_process_fork(odph_linux_process_t *proc, int core)
> +{
> +	return odph_linux_process_fork_n(proc, 1, core);
> +}
> +
> +
> +int odph_linux_process_wait_n(odph_linux_process_t *proc_tbl, int num)
> +{
> +	pid_t pid;
> +	int i, j;
> +	int status;
> +
> +	for (i = 0; i < num; i++) {
> +		pid = wait(&status);
> +
> +		if (pid < 0) {
> +			ODP_ERR("wait() failed\n");
> +			return -1;
> +		}
> +
> +		for (j = 0; j < num; j++) {
> +			if (proc_tbl[j].pid == pid) {
> +				proc_tbl[j].status = status;
> +				break;
> +			}
> +		}
> +
> +		if (j == num) {
> +			ODP_ERR("Bad pid\n");
> +			return -1;
> +		}
> +	}
> +
> +	return 0;
> +}
> diff --git a/platform/linux-generic/odp_thread.c b/platform/linux-generic/odp_thread.c
> index eaa480e..b869b27 100644
> --- a/platform/linux-generic/odp_thread.c
> +++ b/platform/linux-generic/odp_thread.c
> @@ -4,65 +4,111 @@
>    * SPDX-License-Identifier:     BSD-3-Clause
>    */
>   
> +#ifndef _GNU_SOURCE
> +#define _GNU_SOURCE
> +#endif
> +#include <sched.h>
> +
>   #include <odp_thread.h>
>   #include <odp_internal.h>
>   #include <odp_atomic.h>
>   #include <odp_config.h>
> +#include <odp_debug.h>
> +#include <odp_shared_memory.h>
> +#include <odp_align.h>
>   
>   #include <string.h>
>   #include <stdio.h>
> +#include <stdlib.h>
>   
>   
>   typedef struct {
>   	int thr_id;
> -	int phys_core;
> +	int cpu;
> +
> +} thread_state_t;
> +
>   
> -} odp_thread_tbl_t;
> +typedef struct {
> +	thread_state_t   thr[ODP_CONFIG_MAX_THREADS];
> +	odp_atomic_int_t num;
> +
> +} thread_globals_t;
>   
>   
>   /* Globals */
> -static odp_thread_tbl_t odp_thread_tbl[ODP_CONFIG_MAX_THREADS];
> -static odp_atomic_int_t num_threads;
> +static thread_globals_t *thread_globals;
> +
>   
>   /* Thread local */
> -static __thread odp_thread_tbl_t *odp_this_thread;
> +static __thread thread_state_t *this_thread;
>   
>   
> -void odp_thread_init_global(void)
> +int odp_thread_init_global(void)
>   {
> -	memset(odp_thread_tbl, 0, sizeof(odp_thread_tbl));
> -	num_threads = 0;
> -}
> +	odp_shm_t shm;
>   
> +	shm = odp_shm_reserve("odp_thread_globals",
> +			      sizeof(thread_globals_t),
> +			      ODP_CACHE_LINE_SIZE, 0);
>   
> -void odp_thread_init_local(int thr_id)
> -{
> -	odp_this_thread = &odp_thread_tbl[thr_id];
> +	thread_globals = odp_shm_addr(shm);
> +
> +	if (thread_globals == NULL)
> +		return -1;
> +
> +	memset(thread_globals, 0, sizeof(thread_globals_t));
> +	return 0;
>   }
>   
>   
> -int odp_thread_create(int phys_core)
> +static int thread_id(void)
>   {
>   	int id;
> +	int cpu;
> +
> +	id = odp_atomic_fetch_add_int(&thread_globals->num, 1);
>   
> -	id = odp_atomic_fetch_add_int(&num_threads, 1);
> +	if (id >= ODP_CONFIG_MAX_THREADS) {
> +		ODP_ERR("Too many threads\n");
> +		return -1;
> +	}
> +
> +	cpu = sched_getcpu();
>   
> -	if (id < ODP_CONFIG_MAX_THREADS) {
> -		odp_thread_tbl[id].thr_id    = id;
> -		odp_thread_tbl[id].phys_core = phys_core;
> +	if (cpu < 0) {
> +		ODP_ERR("getcpu failed\n");
> +		return -1;
>   	}
>   
> +	thread_globals->thr[id].thr_id = id;
> +	thread_globals->thr[id].cpu    = cpu;
> +
>   	return id;
>   }
>   
>   
> +int odp_thread_init_local(void)
> +{
> +	int id;
> +
> +	id = thread_id();
> +
> +	if (id < 0)
> +		return -1;
> +
> +	this_thread = &thread_globals->thr[id];
> +	return 0;
> +}
> +
> +
>   int odp_thread_id(void)
>   {
> -	return odp_this_thread->thr_id;
> +	return this_thread->thr_id;
>   }
>   
>   
>   int odp_thread_core(void)
>   {
> -	return odp_this_thread->phys_core;
> +	return this_thread->cpu;
>   }
Maxim Uvarov Oct. 3, 2014, 12:44 p.m. UTC | #2
On 10/03/2014 04:44 PM, Maxim Uvarov wrote:
> Thanks Petri, I did some tests with it and it works :) So that it's 
> great that now we can scale for number of processes as well as for 
> number of threads.
>
> Maxim.
>
Forgot to write Merged word.

Maxim.

> On 10/02/2014 04:24 PM, Petri Savolainen wrote:
>> - Added an option to run odp_example as Linux processes (vs. pthreads)
>> - Removed thread dependency from odp_local_init
>> - Added Linux helpers for forking processes
>> - modified odp_thread.c to allocate global variables from shared memory
>>
>> Signed-off-by: Petri Savolainen <petri.savolainen@linaro.org>
>> ---
>>   example/generator/odp_generator.c               |  10 +-
>>   example/ipsec/odp_ipsec.c                       |   7 +-
>>   example/l2fwd/odp_l2fwd.c                       |  11 +-
>>   example/odp_example/odp_example.c               | 184 
>> ++++++++++++++++--------
>>   example/packet/odp_pktio.c                      |  11 +-
>>   example/timer/odp_timer_test.c                  |  14 +-
>>   helper/include/odph_linux.h                     |  66 ++++++++-
>>   platform/linux-generic/include/api/odp_init.h   |   4 +-
>>   platform/linux-generic/include/api/odp_thread.h |  17 +--
>>   platform/linux-generic/include/odp_internal.h   |   4 +-
>>   platform/linux-generic/odp_init.c               |  14 +-
>>   platform/linux-generic/odp_linux.c              | 113 +++++++++++++--
>>   platform/linux-generic/odp_thread.c             |  84 ++++++++---
>>   13 files changed, 401 insertions(+), 138 deletions(-)
>>
>> diff --git a/example/generator/odp_generator.c 
>> b/example/generator/odp_generator.c
>> index 78d9df5..6055324 100644
>> --- a/example/generator/odp_generator.c
>> +++ b/example/generator/odp_generator.c
>> @@ -511,7 +511,6 @@ int main(int argc, char *argv[])
>>   {
>>       odph_linux_pthread_t thread_tbl[MAX_WORKERS];
>>       odp_buffer_pool_t pool;
>> -    int thr_id;
>>       int num_workers;
>>       void *pool_base;
>>       int i;
>> @@ -525,6 +524,11 @@ int main(int argc, char *argv[])
>>           exit(EXIT_FAILURE);
>>       }
>>   +    if (odp_init_local()) {
>> +        ODP_ERR("Error: ODP local init failed.\n");
>> +        exit(EXIT_FAILURE);
>> +    }
>> +
>>       /* init counters */
>>       odp_atomic_init_u64(&counters.seq);
>>       odp_atomic_init_u64(&counters.ip);
>> @@ -574,10 +578,6 @@ int main(int argc, char *argv[])
>>         printf("First core:         %i\n\n", first_core);
>>   -    /* Init this thread */
>> -    thr_id = odp_thread_create(0);
>> -    odp_init_local(thr_id);
>> -
>>       /* Create packet pool */
>>       shm = odp_shm_reserve("shm_packet_pool",
>>                     SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE, 0);
>> diff --git a/example/ipsec/odp_ipsec.c b/example/ipsec/odp_ipsec.c
>> index cd94d9a..fa4100f 100644
>> --- a/example/ipsec/odp_ipsec.c
>> +++ b/example/ipsec/odp_ipsec.c
>> @@ -1166,7 +1166,6 @@ int
>>   main(int argc, char *argv[])
>>   {
>>       odph_linux_pthread_t thread_tbl[MAX_WORKERS];
>> -    int thr_id;
>>       int num_workers;
>>       void *pool_base;
>>       int i;
>> @@ -1182,8 +1181,10 @@ main(int argc, char *argv[])
>>       }
>>         /* Init this thread */
>> -    thr_id = odp_thread_create(0);
>> -    odp_init_local(thr_id);
>> +    if (odp_init_local()) {
>> +        ODP_ERR("Error: ODP local init failed.\n");
>> +        exit(EXIT_FAILURE);
>> +    }
>>         /* Reserve memory for args from shared mem */
>>       shm = odp_shm_reserve("shm_args", sizeof(args_t), 
>> ODP_CACHE_LINE_SIZE,
>> diff --git a/example/l2fwd/odp_l2fwd.c b/example/l2fwd/odp_l2fwd.c
>> index fb325c4..8aa0ba0 100644
>> --- a/example/l2fwd/odp_l2fwd.c
>> +++ b/example/l2fwd/odp_l2fwd.c
>> @@ -311,7 +311,6 @@ int main(int argc, char *argv[])
>>   {
>>       odph_linux_pthread_t thread_tbl[MAX_WORKERS];
>>       odp_buffer_pool_t pool;
>> -    int thr_id;
>>       void *pool_base;
>>       int i;
>>       int first_core;
>> @@ -325,6 +324,12 @@ int main(int argc, char *argv[])
>>           exit(EXIT_FAILURE);
>>       }
>>   +    /* Init this thread */
>> +    if (odp_init_local()) {
>> +        ODP_ERR("Error: ODP local init failed.\n");
>> +        exit(EXIT_FAILURE);
>> +    }
>> +
>>       /* Reserve memory for args from shared mem */
>>       shm = odp_shm_reserve("shm_args", sizeof(args_t),
>>                     ODP_CACHE_LINE_SIZE, 0);
>> @@ -374,10 +379,6 @@ int main(int argc, char *argv[])
>>         printf("First core:         %i\n\n", first_core);
>>   -    /* Init this thread */
>> -    thr_id = odp_thread_create(0);
>> -    odp_init_local(thr_id);
>> -
>>       /* Create packet pool */
>>       shm = odp_shm_reserve("shm_packet_pool",
>>                     SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE, 0);
>> diff --git a/example/odp_example/odp_example.c 
>> b/example/odp_example/odp_example.c
>> index c80dbbc..47d764e 100644
>> --- a/example/odp_example/odp_example.c
>> +++ b/example/odp_example/odp_example.c
>> @@ -46,12 +46,15 @@ typedef struct {
>>     /** Test arguments */
>>   typedef struct {
>> -    int core_count; /**< Core count*/
>> +    int core_count; /**< Core count */
>> +    int proc_mode;  /**< Process mode */
>>   } test_args_t;
>>     -/** @private Barrier for test synchronisation */
>> -static odp_barrier_t test_barrier;
>> +/** Test global variables */
>> +typedef struct {
>> +    odp_barrier_t barrier;/**< @private Barrier for test 
>> synchronisation */
>> +} test_globals_t;
>>       /**
>> @@ -318,7 +321,8 @@ static int test_poll_queue(int thr, 
>> odp_buffer_pool_t msg_pool)
>>    * @return 0 if successful
>>    */
>>   static int test_schedule_one_single(const char *str, int thr,
>> -                    odp_buffer_pool_t msg_pool, int prio)
>> +                    odp_buffer_pool_t msg_pool,
>> +                    int prio, odp_barrier_t *barrier)
>>   {
>>       odp_buffer_t buf;
>>       odp_queue_t queue;
>> @@ -348,7 +352,7 @@ static int test_schedule_one_single(const char 
>> *str, int thr,
>>       ns     = odp_time_cycles_to_ns(cycles);
>>       tot    = i;
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>       clear_sched_queues();
>>         if (tot) {
>> @@ -379,7 +383,8 @@ static int test_schedule_one_single(const char 
>> *str, int thr,
>>    * @return 0 if successful
>>    */
>>   static int test_schedule_one_many(const char *str, int thr,
>> -                  odp_buffer_pool_t msg_pool, int prio)
>> +                  odp_buffer_pool_t msg_pool,
>> +                  int prio, odp_barrier_t *barrier)
>>   {
>>       odp_buffer_t buf;
>>       odp_queue_t queue;
>> @@ -412,7 +417,7 @@ static int test_schedule_one_many(const char 
>> *str, int thr,
>>       ns     = odp_time_cycles_to_ns(cycles);
>>       tot    = i;
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>       clear_sched_queues();
>>         if (tot) {
>> @@ -443,7 +448,8 @@ static int test_schedule_one_many(const char 
>> *str, int thr,
>>    * @return 0 if successful
>>    */
>>   static int test_schedule_single(const char *str, int thr,
>> -                odp_buffer_pool_t msg_pool, int prio)
>> +                odp_buffer_pool_t msg_pool,
>> +                int prio, odp_barrier_t *barrier)
>>   {
>>       odp_buffer_t buf;
>>       odp_queue_t queue;
>> @@ -490,7 +496,7 @@ static int test_schedule_single(const char *str, 
>> int thr,
>>       cycles = odp_time_diff_cycles(t1, t2);
>>       ns     = odp_time_cycles_to_ns(cycles);
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>       clear_sched_queues();
>>         if (tot) {
>> @@ -522,7 +528,8 @@ static int test_schedule_single(const char *str, 
>> int thr,
>>    * @return 0 if successful
>>    */
>>   static int test_schedule_many(const char *str, int thr,
>> -                  odp_buffer_pool_t msg_pool, int prio)
>> +                  odp_buffer_pool_t msg_pool,
>> +                  int prio, odp_barrier_t *barrier)
>>   {
>>       odp_buffer_t buf;
>>       odp_queue_t queue;
>> @@ -572,7 +579,7 @@ static int test_schedule_many(const char *str, 
>> int thr,
>>       cycles = odp_time_diff_cycles(t1, t2);
>>       ns     = odp_time_cycles_to_ns(cycles);
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>       clear_sched_queues();
>>         if (tot) {
>> @@ -600,7 +607,8 @@ static int test_schedule_many(const char *str, 
>> int thr,
>>    * @return 0 if successful
>>    */
>>   static int test_schedule_multi(const char *str, int thr,
>> -                   odp_buffer_pool_t msg_pool, int prio)
>> +                   odp_buffer_pool_t msg_pool,
>> +                   int prio, odp_barrier_t *barrier)
>>   {
>>       odp_buffer_t buf[MULTI_BUFS_MAX];
>>       odp_queue_t queue;
>> @@ -682,7 +690,7 @@ static int test_schedule_multi(const char *str, 
>> int thr,
>>       cycles = odp_time_diff_cycles(t1, t2);
>>       ns     = odp_time_cycles_to_ns(cycles);
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>       clear_sched_queues();
>>         if (tot) {
>> @@ -710,18 +718,31 @@ static void *run_thread(void *arg)
>>   {
>>       int thr;
>>       odp_buffer_pool_t msg_pool;
>> +    odp_shm_t shm;
>> +    test_globals_t *globals;
>> +    odp_barrier_t *barrier;
>>         thr = odp_thread_id();
>>         printf("Thread %i starts on core %i\n", thr, odp_thread_core());
>>   +    shm     = odp_shm_lookup("test_globals");
>> +    globals = odp_shm_addr(shm);
>> +
>> +    if (globals == NULL) {
>> +        ODP_ERR("Shared mem lookup failed\n");
>> +        return NULL;
>> +    }
>> +
>> +    barrier = &globals->barrier;
>> +
>>       /*
>>        * Test barriers back-to-back
>>        */
>> -    odp_barrier_sync(&test_barrier);
>> -    odp_barrier_sync(&test_barrier);
>> -    odp_barrier_sync(&test_barrier);
>> -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>> +    odp_barrier_sync(barrier);
>> +    odp_barrier_sync(barrier);
>> +    odp_barrier_sync(barrier);
>>         /*
>>        * Find the buffer pool
>> @@ -733,83 +754,83 @@ static void *run_thread(void *arg)
>>           return NULL;
>>       }
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>         if (test_alloc_single(thr, msg_pool))
>>           return NULL;
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>         if (test_alloc_multi(thr, msg_pool))
>>           return NULL;
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>         if (test_poll_queue(thr, msg_pool))
>>           return NULL;
>>         /* Low prio */
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>         if (test_schedule_one_single("sched_one_s_lo", thr, msg_pool,
>> -                     ODP_SCHED_PRIO_LOWEST))
>> +                     ODP_SCHED_PRIO_LOWEST, barrier))
>>           return NULL;
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>         if (test_schedule_single("sched_____s_lo", thr, msg_pool,
>> -                 ODP_SCHED_PRIO_LOWEST))
>> +                 ODP_SCHED_PRIO_LOWEST, barrier))
>>           return NULL;
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>         if (test_schedule_one_many("sched_one_m_lo", thr, msg_pool,
>> -                   ODP_SCHED_PRIO_LOWEST))
>> +                   ODP_SCHED_PRIO_LOWEST, barrier))
>>           return NULL;
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>         if (test_schedule_many("sched_____m_lo", thr, msg_pool,
>> -                   ODP_SCHED_PRIO_LOWEST))
>> +                   ODP_SCHED_PRIO_LOWEST, barrier))
>>           return NULL;
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>         if (test_schedule_multi("sched_multi_lo", thr, msg_pool,
>> -                ODP_SCHED_PRIO_LOWEST))
>> +                ODP_SCHED_PRIO_LOWEST, barrier))
>>           return NULL;
>>         /* High prio */
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>         if (test_schedule_one_single("sched_one_s_hi", thr, msg_pool,
>> -                     ODP_SCHED_PRIO_HIGHEST))
>> +                     ODP_SCHED_PRIO_HIGHEST, barrier))
>>           return NULL;
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>         if (test_schedule_single("sched_____s_hi", thr, msg_pool,
>> -                 ODP_SCHED_PRIO_HIGHEST))
>> +                 ODP_SCHED_PRIO_HIGHEST, barrier))
>>           return NULL;
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>         if (test_schedule_one_many("sched_one_m_hi", thr, msg_pool,
>> -                   ODP_SCHED_PRIO_HIGHEST))
>> +                   ODP_SCHED_PRIO_HIGHEST, barrier))
>>           return NULL;
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>         if (test_schedule_many("sched_____m_hi", thr, msg_pool,
>> -                   ODP_SCHED_PRIO_HIGHEST))
>> +                   ODP_SCHED_PRIO_HIGHEST, barrier))
>>           return NULL;
>>   -    odp_barrier_sync(&test_barrier);
>> +    odp_barrier_sync(barrier);
>>         if (test_schedule_multi("sched_multi_hi", thr, msg_pool,
>> -                ODP_SCHED_PRIO_HIGHEST))
>> +                ODP_SCHED_PRIO_HIGHEST, barrier))
>>           return NULL;
>>     @@ -884,6 +905,7 @@ static void print_usage(void)
>>       printf("Options:\n");
>>       printf("  -c, --count <number>    core count, core IDs start 
>> from 1\n");
>>       printf("  -h, --help              this help\n");
>> +    printf("  --proc                  process mode\n");
>>       printf("\n\n");
>>   }
>>   @@ -902,6 +924,7 @@ static void parse_args(int argc, char *argv[], 
>> test_args_t *args)
>>       static struct option longopts[] = {
>>           {"count", required_argument, NULL, 'c'},
>>           {"help", no_argument, NULL, 'h'},
>> +        {"proc", no_argument, NULL, 0},
>>           {NULL, 0, NULL, 0}
>>       };
>>   @@ -912,6 +935,10 @@ static void parse_args(int argc, char *argv[], 
>> test_args_t *args)
>>               break;    /* No more options */
>>             switch (opt) {
>> +        case 0:
>> +            args->proc_mode = 1;
>> +            break;
>> +
>>           case 'c':
>>               args->core_count = atoi(optarg);
>>               break;
>> @@ -935,7 +962,6 @@ int main(int argc, char *argv[])
>>   {
>>       odph_linux_pthread_t thread_tbl[MAX_WORKERS];
>>       test_args_t args;
>> -    int thr_id;
>>       int num_workers;
>>       odp_buffer_pool_t pool;
>>       void *pool_base;
>> @@ -944,16 +970,32 @@ int main(int argc, char *argv[])
>>       int prios;
>>       int first_core;
>>       odp_shm_t shm;
>> +    test_globals_t *globals;
>>   -    printf("\nODP example starts\n");
>> +    printf("\nODP example starts\n\n");
>>         memset(&args, 0, sizeof(args));
>>       parse_args(argc, argv, &args);
>>   +    if (args.proc_mode)
>> +        printf("Process mode\n");
>> +    else
>> +        printf("Thread mode\n");
>> +
>>       memset(thread_tbl, 0, sizeof(thread_tbl));
>>   +    /* ODP global init */
>>       if (odp_init_global()) {
>> -        printf("ODP global init failed.\n");
>> +        ODP_ERR("ODP global init failed.\n");
>> +        return -1;
>> +    }
>> +
>> +    /*
>> +     * Init this thread. It makes also ODP calls when
>> +     * setting up resources for worker threads.
>> +     */
>> +    if (odp_init_local()) {
>> +        ODP_ERR("ODP global init failed.\n");
>>           return -1;
>>       }
>>   @@ -991,16 +1033,22 @@ int main(int argc, char *argv[])
>>         printf("first core:         %i\n", first_core);
>>   -    /*
>> -     * Init this thread. It makes also ODP calls when
>> -     * setting up resources for worker threads.
>> -     */
>> -    thr_id = odp_thread_create(0);
>> -    odp_init_local(thr_id);
>>         /* Test cycle count accuracy */
>>       test_time();
>>   +    shm = odp_shm_reserve("test_globals",
>> +                  sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0);
>> +
>> +    globals = odp_shm_addr(shm);
>> +
>> +    if (globals == NULL) {
>> +        ODP_ERR("Shared memory reserve failed.\n");
>> +        return -1;
>> +    }
>> +
>> +    memset(globals, 0, sizeof(test_globals_t));
>> +
>>       /*
>>        * Create message pool
>>        */
>> @@ -1072,16 +1120,40 @@ int main(int argc, char *argv[])
>>       odp_shm_print_all();
>>         /* Barrier to sync test case execution */
>> -    odp_barrier_init_count(&test_barrier, num_workers);
>> +    odp_barrier_init_count(&globals->barrier, num_workers);
>>   -    /* Create and launch worker threads */
>> -    odph_linux_pthread_create(thread_tbl, num_workers, first_core,
>> -                  run_thread, NULL);
>> +    if (args.proc_mode) {
>> +        int ret;
>> +        odph_linux_process_t proc[MAX_WORKERS];
>>   -    /* Wait for worker threads to exit */
>> -    odph_linux_pthread_join(thread_tbl, num_workers);
>> +        /* Fork worker processes */
>> +        ret = odph_linux_process_fork_n(proc, num_workers,
>> +                        first_core);
>>   -    printf("ODP example complete\n\n");
>> +        if (ret < 0) {
>> +            ODP_ERR("Fork workers failed %i\n", ret);
>> +            return -1;
>> +        }
>> +
>> +        if (ret == 0) {
>> +            /* Child process */
>> +            run_thread(NULL);
>> +        } else {
>> +            /* Parent process */
>> +            odph_linux_process_wait_n(proc, num_workers);
>> +            printf("ODP example complete\n\n");
>> +        }
>> +
>> +    } else {
>> +        /* Create and launch worker threads */
>> +        odph_linux_pthread_create(thread_tbl, num_workers, first_core,
>> +                      run_thread, NULL);
>> +
>> +        /* Wait for worker threads to terminate */
>> +        odph_linux_pthread_join(thread_tbl, num_workers);
>> +
>> +        printf("ODP example complete\n\n");
>> +    }
>>         return 0;
>>   }
>> diff --git a/example/packet/odp_pktio.c b/example/packet/odp_pktio.c
>> index a949a05..145ae47 100644
>> --- a/example/packet/odp_pktio.c
>> +++ b/example/packet/odp_pktio.c
>> @@ -291,7 +291,6 @@ int main(int argc, char *argv[])
>>   {
>>       odph_linux_pthread_t thread_tbl[MAX_WORKERS];
>>       odp_buffer_pool_t pool;
>> -    int thr_id;
>>       int num_workers;
>>       void *pool_base;
>>       int i;
>> @@ -305,6 +304,12 @@ int main(int argc, char *argv[])
>>           exit(EXIT_FAILURE);
>>       }
>>   +    /* Init this thread */
>> +    if (odp_init_local()) {
>> +        ODP_ERR("Error: ODP local init failed.\n");
>> +        exit(EXIT_FAILURE);
>> +    }
>> +
>>       /* Reserve memory for args from shared mem */
>>       shm = odp_shm_reserve("shm_args", sizeof(args_t),
>>                     ODP_CACHE_LINE_SIZE, 0);
>> @@ -344,10 +349,6 @@ int main(int argc, char *argv[])
>>         printf("First core:         %i\n\n", first_core);
>>   -    /* Init this thread */
>> -    thr_id = odp_thread_create(0);
>> -    odp_init_local(thr_id);
>> -
>>       /* Create packet pool */
>>       shm = odp_shm_reserve("shm_packet_pool",
>>                     SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE, 0);
>> diff --git a/example/timer/odp_timer_test.c 
>> b/example/timer/odp_timer_test.c
>> index 6e1715d..87900fc 100644
>> --- a/example/timer/odp_timer_test.c
>> +++ b/example/timer/odp_timer_test.c
>> @@ -240,7 +240,6 @@ int main(int argc, char *argv[])
>>   {
>>       odph_linux_pthread_t thread_tbl[MAX_WORKERS];
>>       test_args_t args;
>> -    int thr_id;
>>       int num_workers;
>>       odp_buffer_pool_t pool;
>>       void *pool_base;
>> @@ -262,6 +261,12 @@ int main(int argc, char *argv[])
>>           return -1;
>>       }
>>   +    /* Init this thread. */
>> +    if (odp_init_local()) {
>> +        printf("ODP local init failed.\n");
>> +        return -1;
>> +    }
>> +
>>       printf("\n");
>>       printf("ODP system info\n");
>>       printf("---------------\n");
>> @@ -302,13 +307,6 @@ int main(int argc, char *argv[])
>>       printf("timeouts:           %i\n", args.tmo_count);
>>         /*
>> -     * Init this thread. It makes also ODP calls when
>> -     * setting up resources for worker threads.
>> -     */
>> -    thr_id = odp_thread_create(0);
>> -    odp_init_local(thr_id);
>> -
>> -    /*
>>        * Create message pool
>>        */
>>       shm = odp_shm_reserve("msg_pool",
>> diff --git a/helper/include/odph_linux.h b/helper/include/odph_linux.h
>> index 1ea349a..8671dc0 100644
>> --- a/helper/include/odph_linux.h
>> +++ b/helper/include/odph_linux.h
>> @@ -10,8 +10,9 @@
>>    *
>>    * ODP Linux helper API
>>    *
>> - * This file is an optional helper to odp.h APIs. Application can 
>> manage
>> - * pthreads also by other means.
>> + * This file is an optional helper to odp.h APIs. These functions 
>> are provided
>> + * to ease common setups in a Linux system. User is free to 
>> implement the same
>> + * setups in otherways (not via this API).
>>    */
>>     #ifndef ODP_LINUX_H_
>> @@ -23,15 +24,24 @@ extern "C" {
>>       #include <pthread.h>
>> +#include <sys/types.h>
>>   -/** Pthread status data */
>> +/** Linux pthread state information */
>>   typedef struct {
>> -    pthread_t      thread; /**< @private Pthread */
>> -    pthread_attr_t attr;   /**< @private Pthread attributes */
>> -
>> +    pthread_t      thread; /**< Pthread ID */
>> +    pthread_attr_t attr;   /**< Pthread attributes */
>> +    int            core;   /**< Core ID */
>>   } odph_linux_pthread_t;
>>     +/** Linux process state information */
>> +typedef struct {
>> +    pid_t pid;      /**< Process ID */
>> +    int   core;     /**< Core ID */
>> +    int   status;   /**< Process state change status */
>> +} odph_linux_process_t;
>> +
>> +
>>   /**
>>    * Creates and launches pthreads
>>    *
>> @@ -61,6 +71,50 @@ void 
>> odph_linux_pthread_create(odph_linux_pthread_t *thread_tbl,
>>   void odph_linux_pthread_join(odph_linux_pthread_t *thread_tbl, int 
>> num);
>>     +/**
>> + * Fork a process
>> + *
>> + * Forks and sets core affinity for the child process
>> + *
>> + * @param proc          Pointer to process state info (for output)
>> + * @param core          Destination core for the child process
>> + *
>> + * @return On success: 1 for the parent, 0 for the child
>> + *         On failure: -1 for the parent, -2 for the child
>> + */
>> +int odph_linux_process_fork(odph_linux_process_t *proc, int core);
>> +
>> +
>> +/**
>> + * Fork a number of processes
>> + *
>> + * Forks and sets core affinity for child processes
>> + *
>> + * @param proc_tbl      Process state info table (for output)
>> + * @param num           Number of processes to create
>> + * @param first_core    Destination core for the first process
>> + *
>> + * @return On success: 1 for the parent, 0 for the child
>> + *         On failure: -1 for the parent, -2 for the child
>> + */
>> +int odph_linux_process_fork_n(odph_linux_process_t *proc_tbl,
>> +                  int num, int first_core);
>> +
>> +
>> +/**
>> + * Wait for a number of processes
>> + *
>> + * Waits for a number of child processes to terminate. Records 
>> process state
>> + * change status into the process state info structure.
>> + *
>> + * @param proc_tbl      Process state info table (previously filled 
>> by fork)
>> + * @param num           Number of processes to wait
>> + *
>> + * @return 0 on success, -1 on failure
>> + */
>> +int odph_linux_process_wait_n(odph_linux_process_t *proc_tbl, int num);
>> +
>> +
>>   #ifdef __cplusplus
>>   }
>>   #endif
>> diff --git a/platform/linux-generic/include/api/odp_init.h 
>> b/platform/linux-generic/include/api/odp_init.h
>> index 490324a..13c8e44 100644
>> --- a/platform/linux-generic/include/api/odp_init.h
>> +++ b/platform/linux-generic/include/api/odp_init.h
>> @@ -41,10 +41,10 @@ int odp_init_global(void);
>>    *
>>    * All threads must call this function before calling
>>    * any other ODP API functions.
>> - * @param thr_id Thread id
>> + *
>>    * @return 0 if successful
>>    */
>> -int odp_init_local(int thr_id);
>> +int odp_init_local(void);
>>       diff --git a/platform/linux-generic/include/api/odp_thread.h 
>> b/platform/linux-generic/include/api/odp_thread.h
>> index e8e8c8a..5567748 100644
>> --- a/platform/linux-generic/include/api/odp_thread.h
>> +++ b/platform/linux-generic/include/api/odp_thread.h
>> @@ -8,7 +8,7 @@
>>   /**
>>    * @file
>>    *
>> - * ODP Linux helper API
>> + * ODP thread API
>>    */
>>     #ifndef ODP_THREAD_H_
>> @@ -19,19 +19,6 @@ extern "C" {
>>   #endif
>>     -
>> -#include <odp_std_types.h>
>> -
>> -
>> -
>> -/**
>> - * Create thread id
>> - *
>> - * @param  core Core dedicated for the thread
>> - * @return New thread id
>> - */
>> -int odp_thread_create(int core);
>> -
>>   /**
>>    * Get thread id
>>    *
>> @@ -41,7 +28,7 @@ int odp_thread_id(void);
>>       /**
>> - * Get thread id
>> + * Get core id
>>    *
>>    * @return Core id where the thread is running currently
>>    */
>> diff --git a/platform/linux-generic/include/odp_internal.h 
>> b/platform/linux-generic/include/odp_internal.h
>> index aa79493..f8c1596 100644
>> --- a/platform/linux-generic/include/odp_internal.h
>> +++ b/platform/linux-generic/include/odp_internal.h
>> @@ -21,8 +21,8 @@ extern "C" {
>>     int odp_system_info_init(void);
>>   -void odp_thread_init_global(void);
>> -void odp_thread_init_local(int thr_id);
>> +int odp_thread_init_global(void);
>> +int odp_thread_init_local(void);
>>     int odp_shm_init_global(void);
>>   int odp_shm_init_local(void);
>> diff --git a/platform/linux-generic/odp_init.c 
>> b/platform/linux-generic/odp_init.c
>> index 5b7e192..55fa53a 100644
>> --- a/platform/linux-generic/odp_init.c
>> +++ b/platform/linux-generic/odp_init.c
>> @@ -11,8 +11,6 @@
>>     int odp_init_global(void)
>>   {
>> -    odp_thread_init_global();
>> -
>>       odp_system_info_init();
>>         if (odp_shm_init_global()) {
>> @@ -20,6 +18,11 @@ int odp_init_global(void)
>>           return -1;
>>       }
>>   +    if (odp_thread_init_global()) {
>> +        ODP_ERR("ODP thread init failed.\n");
>> +        return -1;
>> +    }
>> +
>>       if (odp_buffer_pool_init_global()) {
>>           ODP_ERR("ODP buffer pool init failed.\n");
>>           return -1;
>> @@ -54,9 +57,12 @@ int odp_init_global(void)
>>   }
>>     -int odp_init_local(int thr_id)
>> +int odp_init_local(void)
>>   {
>> -    odp_thread_init_local(thr_id);
>> +    if (odp_thread_init_local()) {
>> +        ODP_ERR("ODP thread local init failed.\n");
>> +        return -1;
>> +    }
>>         if (odp_pktio_init_local()) {
>>           ODP_ERR("ODP packet io local init failed.\n");
>> diff --git a/platform/linux-generic/odp_linux.c 
>> b/platform/linux-generic/odp_linux.c
>> index 9251ec9..cba6637 100644
>> --- a/platform/linux-generic/odp_linux.c
>> +++ b/platform/linux-generic/odp_linux.c
>> @@ -8,12 +8,16 @@
>>   #define _GNU_SOURCE
>>   #endif
>>   #include <sched.h>
>> +#include <unistd.h>
>> +#include <sys/types.h>
>> +#include <sys/wait.h>
>>     #include <stdlib.h>
>>   #include <string.h>
>>   #include <stdio.h>
>>   #include <assert.h>
>>   +
>>   #include <odph_linux.h>
>>   #include <odp_internal.h>
>>   #include <odp_thread.h>
>> @@ -23,7 +27,6 @@
>>       typedef struct {
>> -    int thr_id;
>>       void *(*start_routine) (void *);
>>       void *arg;
>>   @@ -35,9 +38,8 @@ static void *odp_run_start_routine(void *arg)
>>       odp_start_args_t *start_args = arg;
>>         /* ODP thread local init */
>> -    if (odp_init_local(start_args->thr_id)) {
>> -        ODP_ERR("Local init failed for thread: %d\n",
>> -            start_args->thr_id);
>> +    if (odp_init_local()) {
>> +        ODP_ERR("Local init failed\n");
>>           return NULL;
>>       }
>>   @@ -65,9 +67,9 @@ void 
>> odph_linux_pthread_create(odph_linux_pthread_t *thread_tbl, int num,
>>       for (i = 0; i < num; i++) {
>>           pthread_attr_init(&thread_tbl[i].attr);
>>   -        CPU_ZERO(&cpu_set);
>> -
>>           cpu = (first_core + i) % core_count;
>> +        thread_tbl[i].core = cpu;
>> +        CPU_ZERO(&cpu_set);
>>           CPU_SET(cpu, &cpu_set);
>>             pthread_attr_setaffinity_np(&thread_tbl[i].attr,
>> @@ -81,8 +83,6 @@ void odph_linux_pthread_create(odph_linux_pthread_t 
>> *thread_tbl, int num,
>>           start_args->start_routine = start_routine;
>>           start_args->arg           = arg;
>>   -        start_args->thr_id        = odp_thread_create(cpu);
>> -
>>           pthread_create(&thread_tbl[i].thread, &thread_tbl[i].attr,
>>                      odp_run_start_routine, start_args);
>>       }
>> @@ -98,3 +98,100 @@ void odph_linux_pthread_join(odph_linux_pthread_t 
>> *thread_tbl, int num)
>>           pthread_join(thread_tbl[i].thread, NULL);
>>       }
>>   }
>> +
>> +
>> +int odph_linux_process_fork_n(odph_linux_process_t *proc_tbl,
>> +                  int num, int first_core)
>> +{
>> +    cpu_set_t cpu_set;
>> +    pid_t pid;
>> +    int core_count;
>> +    int cpu;
>> +    int i;
>> +
>> +    memset(proc_tbl, 0, num*sizeof(odph_linux_process_t));
>> +
>> +    core_count = odp_sys_core_count();
>> +
>> +    if (first_core < 0 || first_core >= core_count) {
>> +        ODP_ERR("Bad first_core\n");
>> +        return -1;
>> +    }
>> +
>> +    if (num < 0 || num > core_count) {
>> +        ODP_ERR("Bad num\n");
>> +        return -1;
>> +    }
>> +
>> +    for (i = 0; i < num; i++) {
>> +        cpu = (first_core + i) % core_count;
>> +        pid = fork();
>> +
>> +        if (pid < 0) {
>> +            ODP_ERR("fork() failed\n");
>> +            return -1;
>> +        }
>> +
>> +        /* Parent continues to fork */
>> +        if (pid > 0) {
>> +            proc_tbl[i].pid  = pid;
>> +            proc_tbl[i].core = cpu;
>> +            continue;
>> +        }
>> +
>> +        /* Child process */
>> +        CPU_ZERO(&cpu_set);
>> +        CPU_SET(cpu, &cpu_set);
>> +
>> +        if (sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set)) {
>> +            ODP_ERR("sched_setaffinity() failed\n");
>> +            return -2;
>> +        }
>> +
>> +        if (odp_init_local()) {
>> +            ODP_ERR("Local init failed\n");
>> +            return -2;
>> +        }
>> +
>> +        return 0;
>> +    }
>> +
>> +    return 1;
>> +}
>> +
>> +
>> +int odph_linux_process_fork(odph_linux_process_t *proc, int core)
>> +{
>> +    return odph_linux_process_fork_n(proc, 1, core);
>> +}
>> +
>> +
>> +int odph_linux_process_wait_n(odph_linux_process_t *proc_tbl, int num)
>> +{
>> +    pid_t pid;
>> +    int i, j;
>> +    int status;
>> +
>> +    for (i = 0; i < num; i++) {
>> +        pid = wait(&status);
>> +
>> +        if (pid < 0) {
>> +            ODP_ERR("wait() failed\n");
>> +            return -1;
>> +        }
>> +
>> +        for (j = 0; j < num; j++) {
>> +            if (proc_tbl[j].pid == pid) {
>> +                proc_tbl[j].status = status;
>> +                break;
>> +            }
>> +        }
>> +
>> +        if (j == num) {
>> +            ODP_ERR("Bad pid\n");
>> +            return -1;
>> +        }
>> +    }
>> +
>> +    return 0;
>> +}
>> diff --git a/platform/linux-generic/odp_thread.c 
>> b/platform/linux-generic/odp_thread.c
>> index eaa480e..b869b27 100644
>> --- a/platform/linux-generic/odp_thread.c
>> +++ b/platform/linux-generic/odp_thread.c
>> @@ -4,65 +4,111 @@
>>    * SPDX-License-Identifier:     BSD-3-Clause
>>    */
>>   +#ifndef _GNU_SOURCE
>> +#define _GNU_SOURCE
>> +#endif
>> +#include <sched.h>
>> +
>>   #include <odp_thread.h>
>>   #include <odp_internal.h>
>>   #include <odp_atomic.h>
>>   #include <odp_config.h>
>> +#include <odp_debug.h>
>> +#include <odp_shared_memory.h>
>> +#include <odp_align.h>
>>     #include <string.h>
>>   #include <stdio.h>
>> +#include <stdlib.h>
>>       typedef struct {
>>       int thr_id;
>> -    int phys_core;
>> +    int cpu;
>> +
>> +} thread_state_t;
>> +
>>   -} odp_thread_tbl_t;
>> +typedef struct {
>> +    thread_state_t   thr[ODP_CONFIG_MAX_THREADS];
>> +    odp_atomic_int_t num;
>> +
>> +} thread_globals_t;
>>       /* Globals */
>> -static odp_thread_tbl_t odp_thread_tbl[ODP_CONFIG_MAX_THREADS];
>> -static odp_atomic_int_t num_threads;
>> +static thread_globals_t *thread_globals;
>> +
>>     /* Thread local */
>> -static __thread odp_thread_tbl_t *odp_this_thread;
>> +static __thread thread_state_t *this_thread;
>>     -void odp_thread_init_global(void)
>> +int odp_thread_init_global(void)
>>   {
>> -    memset(odp_thread_tbl, 0, sizeof(odp_thread_tbl));
>> -    num_threads = 0;
>> -}
>> +    odp_shm_t shm;
>>   +    shm = odp_shm_reserve("odp_thread_globals",
>> +                  sizeof(thread_globals_t),
>> +                  ODP_CACHE_LINE_SIZE, 0);
>>   -void odp_thread_init_local(int thr_id)
>> -{
>> -    odp_this_thread = &odp_thread_tbl[thr_id];
>> +    thread_globals = odp_shm_addr(shm);
>> +
>> +    if (thread_globals == NULL)
>> +        return -1;
>> +
>> +    memset(thread_globals, 0, sizeof(thread_globals_t));
>> +    return 0;
>>   }
>>     -int odp_thread_create(int phys_core)
>> +static int thread_id(void)
>>   {
>>       int id;
>> +    int cpu;
>> +
>> +    id = odp_atomic_fetch_add_int(&thread_globals->num, 1);
>>   -    id = odp_atomic_fetch_add_int(&num_threads, 1);
>> +    if (id >= ODP_CONFIG_MAX_THREADS) {
>> +        ODP_ERR("Too many threads\n");
>> +        return -1;
>> +    }
>> +
>> +    cpu = sched_getcpu();
>>   -    if (id < ODP_CONFIG_MAX_THREADS) {
>> -        odp_thread_tbl[id].thr_id    = id;
>> -        odp_thread_tbl[id].phys_core = phys_core;
>> +    if (cpu < 0) {
>> +        ODP_ERR("getcpu failed\n");
>> +        return -1;
>>       }
>>   +    thread_globals->thr[id].thr_id = id;
>> +    thread_globals->thr[id].cpu    = cpu;
>> +
>>       return id;
>>   }
>>     +int odp_thread_init_local(void)
>> +{
>> +    int id;
>> +
>> +    id = thread_id();
>> +
>> +    if (id < 0)
>> +        return -1;
>> +
>> +    this_thread = &thread_globals->thr[id];
>> +    return 0;
>> +}
>> +
>> +
>>   int odp_thread_id(void)
>>   {
>> -    return odp_this_thread->thr_id;
>> +    return this_thread->thr_id;
>>   }
>>       int odp_thread_core(void)
>>   {
>> -    return odp_this_thread->phys_core;
>> +    return this_thread->cpu;
>>   }
>
diff mbox

Patch

diff --git a/example/generator/odp_generator.c b/example/generator/odp_generator.c
index 78d9df5..6055324 100644
--- a/example/generator/odp_generator.c
+++ b/example/generator/odp_generator.c
@@ -511,7 +511,6 @@  int main(int argc, char *argv[])
 {
 	odph_linux_pthread_t thread_tbl[MAX_WORKERS];
 	odp_buffer_pool_t pool;
-	int thr_id;
 	int num_workers;
 	void *pool_base;
 	int i;
@@ -525,6 +524,11 @@  int main(int argc, char *argv[])
 		exit(EXIT_FAILURE);
 	}
 
+	if (odp_init_local()) {
+		ODP_ERR("Error: ODP local init failed.\n");
+		exit(EXIT_FAILURE);
+	}
+
 	/* init counters */
 	odp_atomic_init_u64(&counters.seq);
 	odp_atomic_init_u64(&counters.ip);
@@ -574,10 +578,6 @@  int main(int argc, char *argv[])
 
 	printf("First core:         %i\n\n", first_core);
 
-	/* Init this thread */
-	thr_id = odp_thread_create(0);
-	odp_init_local(thr_id);
-
 	/* Create packet pool */
 	shm = odp_shm_reserve("shm_packet_pool",
 			      SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE, 0);
diff --git a/example/ipsec/odp_ipsec.c b/example/ipsec/odp_ipsec.c
index cd94d9a..fa4100f 100644
--- a/example/ipsec/odp_ipsec.c
+++ b/example/ipsec/odp_ipsec.c
@@ -1166,7 +1166,6 @@  int
 main(int argc, char *argv[])
 {
 	odph_linux_pthread_t thread_tbl[MAX_WORKERS];
-	int thr_id;
 	int num_workers;
 	void *pool_base;
 	int i;
@@ -1182,8 +1181,10 @@  main(int argc, char *argv[])
 	}
 
 	/* Init this thread */
-	thr_id = odp_thread_create(0);
-	odp_init_local(thr_id);
+	if (odp_init_local()) {
+		ODP_ERR("Error: ODP local init failed.\n");
+		exit(EXIT_FAILURE);
+	}
 
 	/* Reserve memory for args from shared mem */
 	shm = odp_shm_reserve("shm_args", sizeof(args_t), ODP_CACHE_LINE_SIZE,
diff --git a/example/l2fwd/odp_l2fwd.c b/example/l2fwd/odp_l2fwd.c
index fb325c4..8aa0ba0 100644
--- a/example/l2fwd/odp_l2fwd.c
+++ b/example/l2fwd/odp_l2fwd.c
@@ -311,7 +311,6 @@  int main(int argc, char *argv[])
 {
 	odph_linux_pthread_t thread_tbl[MAX_WORKERS];
 	odp_buffer_pool_t pool;
-	int thr_id;
 	void *pool_base;
 	int i;
 	int first_core;
@@ -325,6 +324,12 @@  int main(int argc, char *argv[])
 		exit(EXIT_FAILURE);
 	}
 
+	/* Init this thread */
+	if (odp_init_local()) {
+		ODP_ERR("Error: ODP local init failed.\n");
+		exit(EXIT_FAILURE);
+	}
+
 	/* Reserve memory for args from shared mem */
 	shm = odp_shm_reserve("shm_args", sizeof(args_t),
 			      ODP_CACHE_LINE_SIZE, 0);
@@ -374,10 +379,6 @@  int main(int argc, char *argv[])
 
 	printf("First core:         %i\n\n", first_core);
 
-	/* Init this thread */
-	thr_id = odp_thread_create(0);
-	odp_init_local(thr_id);
-
 	/* Create packet pool */
 	shm = odp_shm_reserve("shm_packet_pool",
 			      SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE, 0);
diff --git a/example/odp_example/odp_example.c b/example/odp_example/odp_example.c
index c80dbbc..47d764e 100644
--- a/example/odp_example/odp_example.c
+++ b/example/odp_example/odp_example.c
@@ -46,12 +46,15 @@  typedef struct {
 
 /** Test arguments */
 typedef struct {
-	int core_count; /**< Core count*/
+	int core_count; /**< Core count */
+	int proc_mode;  /**< Process mode */
 } test_args_t;
 
 
-/** @private Barrier for test synchronisation */
-static odp_barrier_t test_barrier;
+/** Test global variables */
+typedef struct {
+	odp_barrier_t barrier;/**< @private Barrier for test synchronisation */
+} test_globals_t;
 
 
 /**
@@ -318,7 +321,8 @@  static int test_poll_queue(int thr, odp_buffer_pool_t msg_pool)
  * @return 0 if successful
  */
 static int test_schedule_one_single(const char *str, int thr,
-				    odp_buffer_pool_t msg_pool, int prio)
+				    odp_buffer_pool_t msg_pool,
+				    int prio, odp_barrier_t *barrier)
 {
 	odp_buffer_t buf;
 	odp_queue_t queue;
@@ -348,7 +352,7 @@  static int test_schedule_one_single(const char *str, int thr,
 	ns     = odp_time_cycles_to_ns(cycles);
 	tot    = i;
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 	clear_sched_queues();
 
 	if (tot) {
@@ -379,7 +383,8 @@  static int test_schedule_one_single(const char *str, int thr,
  * @return 0 if successful
  */
 static int test_schedule_one_many(const char *str, int thr,
-				  odp_buffer_pool_t msg_pool, int prio)
+				  odp_buffer_pool_t msg_pool,
+				  int prio, odp_barrier_t *barrier)
 {
 	odp_buffer_t buf;
 	odp_queue_t queue;
@@ -412,7 +417,7 @@  static int test_schedule_one_many(const char *str, int thr,
 	ns     = odp_time_cycles_to_ns(cycles);
 	tot    = i;
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 	clear_sched_queues();
 
 	if (tot) {
@@ -443,7 +448,8 @@  static int test_schedule_one_many(const char *str, int thr,
  * @return 0 if successful
  */
 static int test_schedule_single(const char *str, int thr,
-				odp_buffer_pool_t msg_pool, int prio)
+				odp_buffer_pool_t msg_pool,
+				int prio, odp_barrier_t *barrier)
 {
 	odp_buffer_t buf;
 	odp_queue_t queue;
@@ -490,7 +496,7 @@  static int test_schedule_single(const char *str, int thr,
 	cycles = odp_time_diff_cycles(t1, t2);
 	ns     = odp_time_cycles_to_ns(cycles);
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 	clear_sched_queues();
 
 	if (tot) {
@@ -522,7 +528,8 @@  static int test_schedule_single(const char *str, int thr,
  * @return 0 if successful
  */
 static int test_schedule_many(const char *str, int thr,
-			      odp_buffer_pool_t msg_pool, int prio)
+			      odp_buffer_pool_t msg_pool,
+			      int prio, odp_barrier_t *barrier)
 {
 	odp_buffer_t buf;
 	odp_queue_t queue;
@@ -572,7 +579,7 @@  static int test_schedule_many(const char *str, int thr,
 	cycles = odp_time_diff_cycles(t1, t2);
 	ns     = odp_time_cycles_to_ns(cycles);
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 	clear_sched_queues();
 
 	if (tot) {
@@ -600,7 +607,8 @@  static int test_schedule_many(const char *str, int thr,
  * @return 0 if successful
  */
 static int test_schedule_multi(const char *str, int thr,
-			       odp_buffer_pool_t msg_pool, int prio)
+			       odp_buffer_pool_t msg_pool,
+			       int prio, odp_barrier_t *barrier)
 {
 	odp_buffer_t buf[MULTI_BUFS_MAX];
 	odp_queue_t queue;
@@ -682,7 +690,7 @@  static int test_schedule_multi(const char *str, int thr,
 	cycles = odp_time_diff_cycles(t1, t2);
 	ns     = odp_time_cycles_to_ns(cycles);
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 	clear_sched_queues();
 
 	if (tot) {
@@ -710,18 +718,31 @@  static void *run_thread(void *arg)
 {
 	int thr;
 	odp_buffer_pool_t msg_pool;
+	odp_shm_t shm;
+	test_globals_t *globals;
+	odp_barrier_t *barrier;
 
 	thr = odp_thread_id();
 
 	printf("Thread %i starts on core %i\n", thr, odp_thread_core());
 
+	shm     = odp_shm_lookup("test_globals");
+	globals = odp_shm_addr(shm);
+
+	if (globals == NULL) {
+		ODP_ERR("Shared mem lookup failed\n");
+		return NULL;
+	}
+
+	barrier = &globals->barrier;
+
 	/*
 	 * Test barriers back-to-back
 	 */
-	odp_barrier_sync(&test_barrier);
-	odp_barrier_sync(&test_barrier);
-	odp_barrier_sync(&test_barrier);
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
+	odp_barrier_sync(barrier);
+	odp_barrier_sync(barrier);
+	odp_barrier_sync(barrier);
 
 	/*
 	 * Find the buffer pool
@@ -733,83 +754,83 @@  static void *run_thread(void *arg)
 		return NULL;
 	}
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 
 	if (test_alloc_single(thr, msg_pool))
 		return NULL;
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 
 	if (test_alloc_multi(thr, msg_pool))
 		return NULL;
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 
 	if (test_poll_queue(thr, msg_pool))
 		return NULL;
 
 	/* Low prio */
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 
 	if (test_schedule_one_single("sched_one_s_lo", thr, msg_pool,
-				     ODP_SCHED_PRIO_LOWEST))
+				     ODP_SCHED_PRIO_LOWEST, barrier))
 		return NULL;
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 
 	if (test_schedule_single("sched_____s_lo", thr, msg_pool,
-				 ODP_SCHED_PRIO_LOWEST))
+				 ODP_SCHED_PRIO_LOWEST, barrier))
 		return NULL;
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 
 	if (test_schedule_one_many("sched_one_m_lo", thr, msg_pool,
-				   ODP_SCHED_PRIO_LOWEST))
+				   ODP_SCHED_PRIO_LOWEST, barrier))
 		return NULL;
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 
 	if (test_schedule_many("sched_____m_lo", thr, msg_pool,
-			       ODP_SCHED_PRIO_LOWEST))
+			       ODP_SCHED_PRIO_LOWEST, barrier))
 		return NULL;
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 
 	if (test_schedule_multi("sched_multi_lo", thr, msg_pool,
-				ODP_SCHED_PRIO_LOWEST))
+				ODP_SCHED_PRIO_LOWEST, barrier))
 		return NULL;
 
 	/* High prio */
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 
 	if (test_schedule_one_single("sched_one_s_hi", thr, msg_pool,
-				     ODP_SCHED_PRIO_HIGHEST))
+				     ODP_SCHED_PRIO_HIGHEST, barrier))
 		return NULL;
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 
 	if (test_schedule_single("sched_____s_hi", thr, msg_pool,
-				 ODP_SCHED_PRIO_HIGHEST))
+				 ODP_SCHED_PRIO_HIGHEST, barrier))
 		return NULL;
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 
 	if (test_schedule_one_many("sched_one_m_hi", thr, msg_pool,
-				   ODP_SCHED_PRIO_HIGHEST))
+				   ODP_SCHED_PRIO_HIGHEST, barrier))
 		return NULL;
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 
 	if (test_schedule_many("sched_____m_hi", thr, msg_pool,
-			       ODP_SCHED_PRIO_HIGHEST))
+			       ODP_SCHED_PRIO_HIGHEST, barrier))
 		return NULL;
 
-	odp_barrier_sync(&test_barrier);
+	odp_barrier_sync(barrier);
 
 	if (test_schedule_multi("sched_multi_hi", thr, msg_pool,
-				ODP_SCHED_PRIO_HIGHEST))
+				ODP_SCHED_PRIO_HIGHEST, barrier))
 		return NULL;
 
 
@@ -884,6 +905,7 @@  static void print_usage(void)
 	printf("Options:\n");
 	printf("  -c, --count <number>    core count, core IDs start from 1\n");
 	printf("  -h, --help              this help\n");
+	printf("  --proc                  process mode\n");
 	printf("\n\n");
 }
 
@@ -902,6 +924,7 @@  static void parse_args(int argc, char *argv[], test_args_t *args)
 	static struct option longopts[] = {
 		{"count", required_argument, NULL, 'c'},
 		{"help", no_argument, NULL, 'h'},
+		{"proc", no_argument, NULL, 0},
 		{NULL, 0, NULL, 0}
 	};
 
@@ -912,6 +935,10 @@  static void parse_args(int argc, char *argv[], test_args_t *args)
 			break;	/* No more options */
 
 		switch (opt) {
+		case 0:
+			args->proc_mode = 1;
+			break;
+
 		case 'c':
 			args->core_count = atoi(optarg);
 			break;
@@ -935,7 +962,6 @@  int main(int argc, char *argv[])
 {
 	odph_linux_pthread_t thread_tbl[MAX_WORKERS];
 	test_args_t args;
-	int thr_id;
 	int num_workers;
 	odp_buffer_pool_t pool;
 	void *pool_base;
@@ -944,16 +970,32 @@  int main(int argc, char *argv[])
 	int prios;
 	int first_core;
 	odp_shm_t shm;
+	test_globals_t *globals;
 
-	printf("\nODP example starts\n");
+	printf("\nODP example starts\n\n");
 
 	memset(&args, 0, sizeof(args));
 	parse_args(argc, argv, &args);
 
+	if (args.proc_mode)
+		printf("Process mode\n");
+	else
+		printf("Thread mode\n");
+
 	memset(thread_tbl, 0, sizeof(thread_tbl));
 
+	/* ODP global init */
 	if (odp_init_global()) {
-		printf("ODP global init failed.\n");
+		ODP_ERR("ODP global init failed.\n");
+		return -1;
+	}
+
+	/*
+	 * Init this thread. It makes also ODP calls when
+	 * setting up resources for worker threads.
+	 */
+	if (odp_init_local()) {
+		ODP_ERR("ODP global init failed.\n");
 		return -1;
 	}
 
@@ -991,16 +1033,22 @@  int main(int argc, char *argv[])
 
 	printf("first core:         %i\n", first_core);
 
-	/*
-	 * Init this thread. It makes also ODP calls when
-	 * setting up resources for worker threads.
-	 */
-	thr_id = odp_thread_create(0);
-	odp_init_local(thr_id);
 
 	/* Test cycle count accuracy */
 	test_time();
 
+	shm = odp_shm_reserve("test_globals",
+			      sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0);
+
+	globals = odp_shm_addr(shm);
+
+	if (globals == NULL) {
+		ODP_ERR("Shared memory reserve failed.\n");
+		return -1;
+	}
+
+	memset(globals, 0, sizeof(test_globals_t));
+
 	/*
 	 * Create message pool
 	 */
@@ -1072,16 +1120,40 @@  int main(int argc, char *argv[])
 	odp_shm_print_all();
 
 	/* Barrier to sync test case execution */
-	odp_barrier_init_count(&test_barrier, num_workers);
+	odp_barrier_init_count(&globals->barrier, num_workers);
 
-	/* Create and launch worker threads */
-	odph_linux_pthread_create(thread_tbl, num_workers, first_core,
-				  run_thread, NULL);
+	if (args.proc_mode) {
+		int ret;
+		odph_linux_process_t proc[MAX_WORKERS];
 
-	/* Wait for worker threads to exit */
-	odph_linux_pthread_join(thread_tbl, num_workers);
+		/* Fork worker processes */
+		ret = odph_linux_process_fork_n(proc, num_workers,
+						first_core);
 
-	printf("ODP example complete\n\n");
+		if (ret < 0) {
+			ODP_ERR("Fork workers failed %i\n", ret);
+			return -1;
+		}
+
+		if (ret == 0) {
+			/* Child process */
+			run_thread(NULL);
+		} else {
+			/* Parent process */
+			odph_linux_process_wait_n(proc, num_workers);
+			printf("ODP example complete\n\n");
+		}
+
+	} else {
+		/* Create and launch worker threads */
+		odph_linux_pthread_create(thread_tbl, num_workers, first_core,
+					  run_thread, NULL);
+
+		/* Wait for worker threads to terminate */
+		odph_linux_pthread_join(thread_tbl, num_workers);
+
+		printf("ODP example complete\n\n");
+	}
 
 	return 0;
 }
diff --git a/example/packet/odp_pktio.c b/example/packet/odp_pktio.c
index a949a05..145ae47 100644
--- a/example/packet/odp_pktio.c
+++ b/example/packet/odp_pktio.c
@@ -291,7 +291,6 @@  int main(int argc, char *argv[])
 {
 	odph_linux_pthread_t thread_tbl[MAX_WORKERS];
 	odp_buffer_pool_t pool;
-	int thr_id;
 	int num_workers;
 	void *pool_base;
 	int i;
@@ -305,6 +304,12 @@  int main(int argc, char *argv[])
 		exit(EXIT_FAILURE);
 	}
 
+	/* Init this thread */
+	if (odp_init_local()) {
+		ODP_ERR("Error: ODP local init failed.\n");
+		exit(EXIT_FAILURE);
+	}
+
 	/* Reserve memory for args from shared mem */
 	shm = odp_shm_reserve("shm_args", sizeof(args_t),
 			      ODP_CACHE_LINE_SIZE, 0);
@@ -344,10 +349,6 @@  int main(int argc, char *argv[])
 
 	printf("First core:         %i\n\n", first_core);
 
-	/* Init this thread */
-	thr_id = odp_thread_create(0);
-	odp_init_local(thr_id);
-
 	/* Create packet pool */
 	shm = odp_shm_reserve("shm_packet_pool",
 			      SHM_PKT_POOL_SIZE, ODP_CACHE_LINE_SIZE, 0);
diff --git a/example/timer/odp_timer_test.c b/example/timer/odp_timer_test.c
index 6e1715d..87900fc 100644
--- a/example/timer/odp_timer_test.c
+++ b/example/timer/odp_timer_test.c
@@ -240,7 +240,6 @@  int main(int argc, char *argv[])
 {
 	odph_linux_pthread_t thread_tbl[MAX_WORKERS];
 	test_args_t args;
-	int thr_id;
 	int num_workers;
 	odp_buffer_pool_t pool;
 	void *pool_base;
@@ -262,6 +261,12 @@  int main(int argc, char *argv[])
 		return -1;
 	}
 
+	/* Init this thread. */
+	if (odp_init_local()) {
+		printf("ODP local init failed.\n");
+		return -1;
+	}
+
 	printf("\n");
 	printf("ODP system info\n");
 	printf("---------------\n");
@@ -302,13 +307,6 @@  int main(int argc, char *argv[])
 	printf("timeouts:           %i\n", args.tmo_count);
 
 	/*
-	 * Init this thread. It makes also ODP calls when
-	 * setting up resources for worker threads.
-	 */
-	thr_id = odp_thread_create(0);
-	odp_init_local(thr_id);
-
-	/*
 	 * Create message pool
 	 */
 	shm = odp_shm_reserve("msg_pool",
diff --git a/helper/include/odph_linux.h b/helper/include/odph_linux.h
index 1ea349a..8671dc0 100644
--- a/helper/include/odph_linux.h
+++ b/helper/include/odph_linux.h
@@ -10,8 +10,9 @@ 
  *
  * ODP Linux helper API
  *
- * This file is an optional helper to odp.h APIs. Application can manage
- * pthreads also by other means.
+ * This file is an optional helper to odp.h APIs. These functions are provided
+ * to ease common setups in a Linux system. User is free to implement the same
+ * setups in otherways (not via this API).
  */
 
 #ifndef ODP_LINUX_H_
@@ -23,15 +24,24 @@  extern "C" {
 
 
 #include <pthread.h>
+#include <sys/types.h>
 
-/** Pthread status data */
+/** Linux pthread state information */
 typedef struct {
-	pthread_t      thread; /**< @private Pthread */
-	pthread_attr_t attr;   /**< @private Pthread attributes */
-
+	pthread_t      thread; /**< Pthread ID */
+	pthread_attr_t attr;   /**< Pthread attributes */
+	int            core;   /**< Core ID */
 } odph_linux_pthread_t;
 
 
+/** Linux process state information */
+typedef struct {
+	pid_t pid;      /**< Process ID */
+	int   core;     /**< Core ID */
+	int   status;   /**< Process state change status */
+} odph_linux_process_t;
+
+
 /**
  * Creates and launches pthreads
  *
@@ -61,6 +71,50 @@  void odph_linux_pthread_create(odph_linux_pthread_t *thread_tbl,
 void odph_linux_pthread_join(odph_linux_pthread_t *thread_tbl, int num);
 
 
+/**
+ * Fork a process
+ *
+ * Forks and sets core affinity for the child process
+ *
+ * @param proc          Pointer to process state info (for output)
+ * @param core          Destination core for the child process
+ *
+ * @return On success: 1 for the parent, 0 for the child
+ *         On failure: -1 for the parent, -2 for the child
+ */
+int odph_linux_process_fork(odph_linux_process_t *proc, int core);
+
+
+/**
+ * Fork a number of processes
+ *
+ * Forks and sets core affinity for child processes
+ *
+ * @param proc_tbl      Process state info table (for output)
+ * @param num           Number of processes to create
+ * @param first_core    Destination core for the first process
+ *
+ * @return On success: 1 for the parent, 0 for the child
+ *         On failure: -1 for the parent, -2 for the child
+ */
+int odph_linux_process_fork_n(odph_linux_process_t *proc_tbl,
+			      int num, int first_core);
+
+
+/**
+ * Wait for a number of processes
+ *
+ * Waits for a number of child processes to terminate. Records process state
+ * change status into the process state info structure.
+ *
+ * @param proc_tbl      Process state info table (previously filled by fork)
+ * @param num           Number of processes to wait
+ *
+ * @return 0 on success, -1 on failure
+ */
+int odph_linux_process_wait_n(odph_linux_process_t *proc_tbl, int num);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/platform/linux-generic/include/api/odp_init.h b/platform/linux-generic/include/api/odp_init.h
index 490324a..13c8e44 100644
--- a/platform/linux-generic/include/api/odp_init.h
+++ b/platform/linux-generic/include/api/odp_init.h
@@ -41,10 +41,10 @@  int odp_init_global(void);
  *
  * All threads must call this function before calling
  * any other ODP API functions.
- * @param thr_id Thread id
+ *
  * @return 0 if successful
  */
-int odp_init_local(int thr_id);
+int odp_init_local(void);
 
 
 
diff --git a/platform/linux-generic/include/api/odp_thread.h b/platform/linux-generic/include/api/odp_thread.h
index e8e8c8a..5567748 100644
--- a/platform/linux-generic/include/api/odp_thread.h
+++ b/platform/linux-generic/include/api/odp_thread.h
@@ -8,7 +8,7 @@ 
 /**
  * @file
  *
- * ODP Linux helper API
+ * ODP thread API
  */
 
 #ifndef ODP_THREAD_H_
@@ -19,19 +19,6 @@  extern "C" {
 #endif
 
 
-
-#include <odp_std_types.h>
-
-
-
-/**
- * Create thread id
- *
- * @param  core Core dedicated for the thread
- * @return New thread id
- */
-int odp_thread_create(int core);
-
 /**
  * Get thread id
  *
@@ -41,7 +28,7 @@  int odp_thread_id(void);
 
 
 /**
- * Get thread id
+ * Get core id
  *
  * @return Core id where the thread is running currently
  */
diff --git a/platform/linux-generic/include/odp_internal.h b/platform/linux-generic/include/odp_internal.h
index aa79493..f8c1596 100644
--- a/platform/linux-generic/include/odp_internal.h
+++ b/platform/linux-generic/include/odp_internal.h
@@ -21,8 +21,8 @@  extern "C" {
 
 int odp_system_info_init(void);
 
-void odp_thread_init_global(void);
-void odp_thread_init_local(int thr_id);
+int odp_thread_init_global(void);
+int odp_thread_init_local(void);
 
 int odp_shm_init_global(void);
 int odp_shm_init_local(void);
diff --git a/platform/linux-generic/odp_init.c b/platform/linux-generic/odp_init.c
index 5b7e192..55fa53a 100644
--- a/platform/linux-generic/odp_init.c
+++ b/platform/linux-generic/odp_init.c
@@ -11,8 +11,6 @@ 
 
 int odp_init_global(void)
 {
-	odp_thread_init_global();
-
 	odp_system_info_init();
 
 	if (odp_shm_init_global()) {
@@ -20,6 +18,11 @@  int odp_init_global(void)
 		return -1;
 	}
 
+	if (odp_thread_init_global()) {
+		ODP_ERR("ODP thread init failed.\n");
+		return -1;
+	}
+
 	if (odp_buffer_pool_init_global()) {
 		ODP_ERR("ODP buffer pool init failed.\n");
 		return -1;
@@ -54,9 +57,12 @@  int odp_init_global(void)
 }
 
 
-int odp_init_local(int thr_id)
+int odp_init_local(void)
 {
-	odp_thread_init_local(thr_id);
+	if (odp_thread_init_local()) {
+		ODP_ERR("ODP thread local init failed.\n");
+		return -1;
+	}
 
 	if (odp_pktio_init_local()) {
 		ODP_ERR("ODP packet io local init failed.\n");
diff --git a/platform/linux-generic/odp_linux.c b/platform/linux-generic/odp_linux.c
index 9251ec9..cba6637 100644
--- a/platform/linux-generic/odp_linux.c
+++ b/platform/linux-generic/odp_linux.c
@@ -8,12 +8,16 @@ 
 #define _GNU_SOURCE
 #endif
 #include <sched.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
 
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include <assert.h>
 
+
 #include <odph_linux.h>
 #include <odp_internal.h>
 #include <odp_thread.h>
@@ -23,7 +27,6 @@ 
 
 
 typedef struct {
-	int thr_id;
 	void *(*start_routine) (void *);
 	void *arg;
 
@@ -35,9 +38,8 @@  static void *odp_run_start_routine(void *arg)
 	odp_start_args_t *start_args = arg;
 
 	/* ODP thread local init */
-	if (odp_init_local(start_args->thr_id)) {
-		ODP_ERR("Local init failed for thread: %d\n",
-			start_args->thr_id);
+	if (odp_init_local()) {
+		ODP_ERR("Local init failed\n");
 		return NULL;
 	}
 
@@ -65,9 +67,9 @@  void odph_linux_pthread_create(odph_linux_pthread_t *thread_tbl, int num,
 	for (i = 0; i < num; i++) {
 		pthread_attr_init(&thread_tbl[i].attr);
 
-		CPU_ZERO(&cpu_set);
-
 		cpu = (first_core + i) % core_count;
+		thread_tbl[i].core = cpu;
+		CPU_ZERO(&cpu_set);
 		CPU_SET(cpu, &cpu_set);
 
 		pthread_attr_setaffinity_np(&thread_tbl[i].attr,
@@ -81,8 +83,6 @@  void odph_linux_pthread_create(odph_linux_pthread_t *thread_tbl, int num,
 		start_args->start_routine = start_routine;
 		start_args->arg           = arg;
 
-		start_args->thr_id        = odp_thread_create(cpu);
-
 		pthread_create(&thread_tbl[i].thread, &thread_tbl[i].attr,
 			       odp_run_start_routine, start_args);
 	}
@@ -98,3 +98,100 @@  void odph_linux_pthread_join(odph_linux_pthread_t *thread_tbl, int num)
 		pthread_join(thread_tbl[i].thread, NULL);
 	}
 }
+
+
+int odph_linux_process_fork_n(odph_linux_process_t *proc_tbl,
+			      int num, int first_core)
+{
+	cpu_set_t cpu_set;
+	pid_t pid;
+	int core_count;
+	int cpu;
+	int i;
+
+	memset(proc_tbl, 0, num*sizeof(odph_linux_process_t));
+
+	core_count = odp_sys_core_count();
+
+	if (first_core < 0 || first_core >= core_count) {
+		ODP_ERR("Bad first_core\n");
+		return -1;
+	}
+
+	if (num < 0 || num > core_count) {
+		ODP_ERR("Bad num\n");
+		return -1;
+	}
+
+	for (i = 0; i < num; i++) {
+		cpu = (first_core + i) % core_count;
+		pid = fork();
+
+		if (pid < 0) {
+			ODP_ERR("fork() failed\n");
+			return -1;
+		}
+
+		/* Parent continues to fork */
+		if (pid > 0) {
+			proc_tbl[i].pid  = pid;
+			proc_tbl[i].core = cpu;
+			continue;
+		}
+
+		/* Child process */
+		CPU_ZERO(&cpu_set);
+		CPU_SET(cpu, &cpu_set);
+
+		if (sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set)) {
+			ODP_ERR("sched_setaffinity() failed\n");
+			return -2;
+		}
+
+		if (odp_init_local()) {
+			ODP_ERR("Local init failed\n");
+			return -2;
+		}
+
+		return 0;
+	}
+
+	return 1;
+}
+
+
+int odph_linux_process_fork(odph_linux_process_t *proc, int core)
+{
+	return odph_linux_process_fork_n(proc, 1, core);
+}
+
+
+int odph_linux_process_wait_n(odph_linux_process_t *proc_tbl, int num)
+{
+	pid_t pid;
+	int i, j;
+	int status;
+
+	for (i = 0; i < num; i++) {
+		pid = wait(&status);
+
+		if (pid < 0) {
+			ODP_ERR("wait() failed\n");
+			return -1;
+		}
+
+		for (j = 0; j < num; j++) {
+			if (proc_tbl[j].pid == pid) {
+				proc_tbl[j].status = status;
+				break;
+			}
+		}
+
+		if (j == num) {
+			ODP_ERR("Bad pid\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
diff --git a/platform/linux-generic/odp_thread.c b/platform/linux-generic/odp_thread.c
index eaa480e..b869b27 100644
--- a/platform/linux-generic/odp_thread.c
+++ b/platform/linux-generic/odp_thread.c
@@ -4,65 +4,111 @@ 
  * SPDX-License-Identifier:     BSD-3-Clause
  */
 
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <sched.h>
+
 #include <odp_thread.h>
 #include <odp_internal.h>
 #include <odp_atomic.h>
 #include <odp_config.h>
+#include <odp_debug.h>
+#include <odp_shared_memory.h>
+#include <odp_align.h>
 
 #include <string.h>
 #include <stdio.h>
+#include <stdlib.h>
 
 
 typedef struct {
 	int thr_id;
-	int phys_core;
+	int cpu;
+
+} thread_state_t;
+
 
-} odp_thread_tbl_t;
+typedef struct {
+	thread_state_t   thr[ODP_CONFIG_MAX_THREADS];
+	odp_atomic_int_t num;
+
+} thread_globals_t;
 
 
 /* Globals */
-static odp_thread_tbl_t odp_thread_tbl[ODP_CONFIG_MAX_THREADS];
-static odp_atomic_int_t num_threads;
+static thread_globals_t *thread_globals;
+
 
 /* Thread local */
-static __thread odp_thread_tbl_t *odp_this_thread;
+static __thread thread_state_t *this_thread;
 
 
-void odp_thread_init_global(void)
+int odp_thread_init_global(void)
 {
-	memset(odp_thread_tbl, 0, sizeof(odp_thread_tbl));
-	num_threads = 0;
-}
+	odp_shm_t shm;
 
+	shm = odp_shm_reserve("odp_thread_globals",
+			      sizeof(thread_globals_t),
+			      ODP_CACHE_LINE_SIZE, 0);
 
-void odp_thread_init_local(int thr_id)
-{
-	odp_this_thread = &odp_thread_tbl[thr_id];
+	thread_globals = odp_shm_addr(shm);
+
+	if (thread_globals == NULL)
+		return -1;
+
+	memset(thread_globals, 0, sizeof(thread_globals_t));
+	return 0;
 }
 
 
-int odp_thread_create(int phys_core)
+static int thread_id(void)
 {
 	int id;
+	int cpu;
+
+	id = odp_atomic_fetch_add_int(&thread_globals->num, 1);
 
-	id = odp_atomic_fetch_add_int(&num_threads, 1);
+	if (id >= ODP_CONFIG_MAX_THREADS) {
+		ODP_ERR("Too many threads\n");
+		return -1;
+	}
+
+	cpu = sched_getcpu();
 
-	if (id < ODP_CONFIG_MAX_THREADS) {
-		odp_thread_tbl[id].thr_id    = id;
-		odp_thread_tbl[id].phys_core = phys_core;
+	if (cpu < 0) {
+		ODP_ERR("getcpu failed\n");
+		return -1;
 	}
 
+	thread_globals->thr[id].thr_id = id;
+	thread_globals->thr[id].cpu    = cpu;
+
 	return id;
 }
 
 
+int odp_thread_init_local(void)
+{
+	int id;
+
+	id = thread_id();
+
+	if (id < 0)
+		return -1;
+
+	this_thread = &thread_globals->thr[id];
+	return 0;
+}
+
+
 int odp_thread_id(void)
 {
-	return odp_this_thread->thr_id;
+	return this_thread->thr_id;
 }
 
 
 int odp_thread_core(void)
 {
-	return odp_this_thread->phys_core;
+	return this_thread->cpu;
 }