xref: /dpdk/examples/pipeline/thread.c (revision 7e06c0de1952d3109a5b0c4779d7e7d8059c9d78)
1b77f6600SCristian Dumitrescu /* SPDX-License-Identifier: BSD-3-Clause
2b77f6600SCristian Dumitrescu  * Copyright(c) 2020 Intel Corporation
3b77f6600SCristian Dumitrescu  */
4b77f6600SCristian Dumitrescu 
5b77f6600SCristian Dumitrescu #include <stdlib.h>
641f5dfcbSCristian Dumitrescu #include <errno.h>
7b77f6600SCristian Dumitrescu 
841f5dfcbSCristian Dumitrescu #include <rte_atomic.h>
9b77f6600SCristian Dumitrescu #include <rte_common.h>
10b77f6600SCristian Dumitrescu #include <rte_lcore.h>
11b77f6600SCristian Dumitrescu 
12b77f6600SCristian Dumitrescu #include "obj.h"
13b77f6600SCristian Dumitrescu #include "thread.h"
14b77f6600SCristian Dumitrescu 
15b77f6600SCristian Dumitrescu #ifndef THREAD_PIPELINES_MAX
16b77f6600SCristian Dumitrescu #define THREAD_PIPELINES_MAX                               256
17b77f6600SCristian Dumitrescu #endif
18b77f6600SCristian Dumitrescu 
19ce5d73a9SCristian Dumitrescu #ifndef THREAD_BLOCKS_MAX
20ce5d73a9SCristian Dumitrescu #define THREAD_BLOCKS_MAX                                  256
21ce5d73a9SCristian Dumitrescu #endif
22ce5d73a9SCristian Dumitrescu 
230b50ea60SCristian Dumitrescu /* Pipeline instruction quanta: Needs to be big enough to do some meaningful
240b50ea60SCristian Dumitrescu  * work, but not too big to avoid starving any other pipelines mapped to the
250b50ea60SCristian Dumitrescu  * same thread. For a pipeline that executes 10 instructions per packet, a
260b50ea60SCristian Dumitrescu  * quanta of 1000 instructions equates to processing 100 packets.
270b50ea60SCristian Dumitrescu  */
280b50ea60SCristian Dumitrescu #ifndef PIPELINE_INSTR_QUANTA
290b50ea60SCristian Dumitrescu #define PIPELINE_INSTR_QUANTA                              1000
300b50ea60SCristian Dumitrescu #endif
310b50ea60SCristian Dumitrescu 
32b77f6600SCristian Dumitrescu /**
3341f5dfcbSCristian Dumitrescu  * In this design, there is a single control plane (CP) thread and one or multiple data plane (DP)
3441f5dfcbSCristian Dumitrescu  * threads. Each DP thread can run up to THREAD_PIPELINES_MAX pipelines and up to THREAD_BLOCKS_MAX
3541f5dfcbSCristian Dumitrescu  * blocks.
3641f5dfcbSCristian Dumitrescu  *
3741f5dfcbSCristian Dumitrescu  * The pipelines and blocks are single threaded, meaning that a given pipeline/block can be run by a
3841f5dfcbSCristian Dumitrescu  * single thread at any given time, so the same pipeline/block cannot show up in the list of
3941f5dfcbSCristian Dumitrescu  * pipelines/blocks of more than one thread at any specific moment.
4041f5dfcbSCristian Dumitrescu  *
4141f5dfcbSCristian Dumitrescu  * Each DP thread has its own context (struct thread instance), which it shares with the CP thread:
4241f5dfcbSCristian Dumitrescu  *  - Read-write by the CP thread;
4341f5dfcbSCristian Dumitrescu  *  - Read-only by the DP thread.
44b77f6600SCristian Dumitrescu  */
45ce5d73a9SCristian Dumitrescu struct block {
46ce5d73a9SCristian Dumitrescu 	block_run_f block_func;
47ce5d73a9SCristian Dumitrescu 	void *block;
48ce5d73a9SCristian Dumitrescu };
49ce5d73a9SCristian Dumitrescu 
50*7e06c0deSTyler Retzlaff struct __rte_cache_aligned thread {
5141f5dfcbSCristian Dumitrescu 	struct rte_swx_pipeline *pipelines[THREAD_PIPELINES_MAX];
52ce5d73a9SCristian Dumitrescu 	struct block *blocks[THREAD_BLOCKS_MAX];
5341f5dfcbSCristian Dumitrescu 	volatile uint64_t n_pipelines;
54ce5d73a9SCristian Dumitrescu 	volatile uint64_t n_blocks;
5541f5dfcbSCristian Dumitrescu 	int enabled;
56*7e06c0deSTyler Retzlaff };
57b77f6600SCristian Dumitrescu 
5841f5dfcbSCristian Dumitrescu static struct thread threads[RTE_MAX_LCORE];
59b77f6600SCristian Dumitrescu 
60b77f6600SCristian Dumitrescu /**
6141f5dfcbSCristian Dumitrescu  * Control plane (CP) thread.
62b77f6600SCristian Dumitrescu  */
63b77f6600SCristian Dumitrescu int
thread_init(void)64b77f6600SCristian Dumitrescu thread_init(void)
65b77f6600SCristian Dumitrescu {
6641f5dfcbSCristian Dumitrescu 	uint32_t thread_id;
67ce5d73a9SCristian Dumitrescu 	int status = 0;
68b77f6600SCristian Dumitrescu 
6941f5dfcbSCristian Dumitrescu 	RTE_LCORE_FOREACH_WORKER(thread_id) {
7041f5dfcbSCristian Dumitrescu 		struct thread *t = &threads[thread_id];
71ce5d73a9SCristian Dumitrescu 		uint32_t i;
72b77f6600SCristian Dumitrescu 
73b77f6600SCristian Dumitrescu 		t->enabled = 1;
74ce5d73a9SCristian Dumitrescu 
75ce5d73a9SCristian Dumitrescu 		for (i = 0; i < THREAD_BLOCKS_MAX; i++) {
76ce5d73a9SCristian Dumitrescu 			struct block *b;
77ce5d73a9SCristian Dumitrescu 
78ce5d73a9SCristian Dumitrescu 			b = calloc(1, sizeof(struct block));
79ce5d73a9SCristian Dumitrescu 			if (!b) {
80ce5d73a9SCristian Dumitrescu 				status = -ENOMEM;
81ce5d73a9SCristian Dumitrescu 				goto error;
82ce5d73a9SCristian Dumitrescu 			}
83ce5d73a9SCristian Dumitrescu 
84ce5d73a9SCristian Dumitrescu 			t->blocks[i] = b;
85ce5d73a9SCristian Dumitrescu 		}
86b77f6600SCristian Dumitrescu 	}
87b77f6600SCristian Dumitrescu 
88b77f6600SCristian Dumitrescu 	return 0;
89ce5d73a9SCristian Dumitrescu 
90ce5d73a9SCristian Dumitrescu error:
91ce5d73a9SCristian Dumitrescu 	RTE_LCORE_FOREACH_WORKER(thread_id) {
92ce5d73a9SCristian Dumitrescu 		struct thread *t = &threads[thread_id];
93ce5d73a9SCristian Dumitrescu 		uint32_t i;
94ce5d73a9SCristian Dumitrescu 
95ce5d73a9SCristian Dumitrescu 		t->enabled = 0;
96ce5d73a9SCristian Dumitrescu 
97ce5d73a9SCristian Dumitrescu 		for (i = 0; i < THREAD_BLOCKS_MAX; i++) {
98ce5d73a9SCristian Dumitrescu 			free(t->blocks[i]);
99ce5d73a9SCristian Dumitrescu 			t->blocks[i] = NULL;
100ce5d73a9SCristian Dumitrescu 		}
101ce5d73a9SCristian Dumitrescu 	}
102ce5d73a9SCristian Dumitrescu 
103ce5d73a9SCristian Dumitrescu 	return status;
104b77f6600SCristian Dumitrescu }
105b77f6600SCristian Dumitrescu 
10641f5dfcbSCristian Dumitrescu static uint32_t
pipeline_find(struct rte_swx_pipeline * p)10741f5dfcbSCristian Dumitrescu pipeline_find(struct rte_swx_pipeline *p)
108b77f6600SCristian Dumitrescu {
10941f5dfcbSCristian Dumitrescu 	uint32_t thread_id;
110b77f6600SCristian Dumitrescu 
11141f5dfcbSCristian Dumitrescu 	for (thread_id = 0; thread_id < RTE_MAX_LCORE; thread_id++) {
11241f5dfcbSCristian Dumitrescu 		struct thread *t = &threads[thread_id];
113b9559f94SCristian Dumitrescu 		uint32_t i;
114b9559f94SCristian Dumitrescu 
115b9559f94SCristian Dumitrescu 		if (!t->enabled)
116b77f6600SCristian Dumitrescu 			continue;
117b77f6600SCristian Dumitrescu 
11841f5dfcbSCristian Dumitrescu 		for (i = 0; i < t->n_pipelines; i++)
11941f5dfcbSCristian Dumitrescu 			if (t->pipelines[i] == p)
120b77f6600SCristian Dumitrescu 				break;
121b77f6600SCristian Dumitrescu 	}
122b77f6600SCristian Dumitrescu 
12341f5dfcbSCristian Dumitrescu 	return thread_id;
124b77f6600SCristian Dumitrescu }
125b77f6600SCristian Dumitrescu 
126ce5d73a9SCristian Dumitrescu static uint32_t
block_find(void * b)127ce5d73a9SCristian Dumitrescu block_find(void *b)
128ce5d73a9SCristian Dumitrescu {
129ce5d73a9SCristian Dumitrescu 	uint32_t thread_id;
130ce5d73a9SCristian Dumitrescu 
131ce5d73a9SCristian Dumitrescu 	for (thread_id = 0; thread_id < RTE_MAX_LCORE; thread_id++) {
132ce5d73a9SCristian Dumitrescu 		struct thread *t = &threads[thread_id];
133ce5d73a9SCristian Dumitrescu 		uint32_t i;
134ce5d73a9SCristian Dumitrescu 
135ce5d73a9SCristian Dumitrescu 		if (!t->enabled)
136ce5d73a9SCristian Dumitrescu 			continue;
137ce5d73a9SCristian Dumitrescu 
138ce5d73a9SCristian Dumitrescu 		for (i = 0; i < t->n_blocks; i++)
139ce5d73a9SCristian Dumitrescu 			if (t->blocks[i]->block == b)
140ce5d73a9SCristian Dumitrescu 				break;
141ce5d73a9SCristian Dumitrescu 	}
142ce5d73a9SCristian Dumitrescu 
143ce5d73a9SCristian Dumitrescu 	return thread_id;
144ce5d73a9SCristian Dumitrescu }
145ce5d73a9SCristian Dumitrescu 
14641f5dfcbSCristian Dumitrescu /**
14741f5dfcbSCristian Dumitrescu  * Enable a given pipeline to run on a specific DP thread.
14841f5dfcbSCristian Dumitrescu  *
14941f5dfcbSCristian Dumitrescu  * CP thread:
15041f5dfcbSCristian Dumitrescu  *  - Adds a new pipeline to the end of the DP thread pipeline list (t->pipelines[]);
15141f5dfcbSCristian Dumitrescu  *  - Increments the DP thread number of pipelines (t->n_pipelines). It is important to make sure
15241f5dfcbSCristian Dumitrescu  *    that t->pipelines[] update is completed BEFORE the t->n_pipelines update, hence the memory
15341f5dfcbSCristian Dumitrescu  *    write barrier used below.
15441f5dfcbSCristian Dumitrescu  *
15541f5dfcbSCristian Dumitrescu  * DP thread:
15641f5dfcbSCristian Dumitrescu  *  - Reads t->n_pipelines before starting every new iteration through t->pipelines[]. It detects
15741f5dfcbSCristian Dumitrescu  *    the new pipeline when it sees the updated t->n_pipelines value;
15841f5dfcbSCristian Dumitrescu  *  - If somehow the above condition is not met, so t->n_pipelines update is incorrectly taking
15941f5dfcbSCristian Dumitrescu  *    place before the t->pipelines[] update is completed, then the DP thread will use an incorrect
16041f5dfcbSCristian Dumitrescu  *    handle for the new pipeline, which can result in memory corruption or segmentation fault.
16141f5dfcbSCristian Dumitrescu  */
16241f5dfcbSCristian Dumitrescu int
pipeline_enable(struct rte_swx_pipeline * p,uint32_t thread_id)16341f5dfcbSCristian Dumitrescu pipeline_enable(struct rte_swx_pipeline *p, uint32_t thread_id)
16441f5dfcbSCristian Dumitrescu {
16541f5dfcbSCristian Dumitrescu 	struct thread *t;
16641f5dfcbSCristian Dumitrescu 	uint64_t n_pipelines;
167b77f6600SCristian Dumitrescu 
16841f5dfcbSCristian Dumitrescu 	/* Check input params */
16941f5dfcbSCristian Dumitrescu 	if (!p || thread_id >= RTE_MAX_LCORE)
17041f5dfcbSCristian Dumitrescu 		return -EINVAL;
171b77f6600SCristian Dumitrescu 
17241f5dfcbSCristian Dumitrescu 	if (pipeline_find(p) < RTE_MAX_LCORE)
17341f5dfcbSCristian Dumitrescu 		return -EEXIST;
174b77f6600SCristian Dumitrescu 
17541f5dfcbSCristian Dumitrescu 	t = &threads[thread_id];
17641f5dfcbSCristian Dumitrescu 	if (!t->enabled)
17741f5dfcbSCristian Dumitrescu 		return -EINVAL;
178b77f6600SCristian Dumitrescu 
17941f5dfcbSCristian Dumitrescu 	n_pipelines = t->n_pipelines;
180b77f6600SCristian Dumitrescu 
18141f5dfcbSCristian Dumitrescu 	/* Check there is room for at least one more pipeline. */
18241f5dfcbSCristian Dumitrescu 	if (n_pipelines >= THREAD_PIPELINES_MAX)
18341f5dfcbSCristian Dumitrescu 		return -ENOSPC;
18441f5dfcbSCristian Dumitrescu 
18541f5dfcbSCristian Dumitrescu 	/* Install the new pipeline. */
18641f5dfcbSCristian Dumitrescu 	t->pipelines[n_pipelines] = p;
18741f5dfcbSCristian Dumitrescu 	rte_wmb();
18841f5dfcbSCristian Dumitrescu 	t->n_pipelines = n_pipelines + 1;
189b77f6600SCristian Dumitrescu 
190b77f6600SCristian Dumitrescu 	return 0;
191b77f6600SCristian Dumitrescu }
192b77f6600SCristian Dumitrescu 
193b77f6600SCristian Dumitrescu /**
19441f5dfcbSCristian Dumitrescu  * Disable a given pipeline from running on any DP thread.
19541f5dfcbSCristian Dumitrescu  *
19641f5dfcbSCristian Dumitrescu  * CP thread:
19741f5dfcbSCristian Dumitrescu  *  - Detects the thread that is running the given pipeline, if any;
19841f5dfcbSCristian Dumitrescu  *  - Writes the last pipeline handle (pipeline_last = t->pipelines[t->n_pipelines - 1]) on the
19941f5dfcbSCristian Dumitrescu  *    position of the pipeline to be disabled (t->pipelines[i] = pipeline_last) and decrements the
20041f5dfcbSCristian Dumitrescu  *    number of pipelines running on the current thread (t->n_pipelines--). This approach makes sure
20141f5dfcbSCristian Dumitrescu  *    that no holes with invalid locations are ever developed within the t->pipelines[] array.
20241f5dfcbSCristian Dumitrescu  *  - If the memory barrier below is present, then t->n_pipelines update is guaranteed to take place
20341f5dfcbSCristian Dumitrescu  *    after the t->pipelines[] update is completed. The possible DP thread behaviors are detailed
20441f5dfcbSCristian Dumitrescu  *    below, which are all valid:
20541f5dfcbSCristian Dumitrescu  *     - Not run the removed pipeline at all, run all the other pipelines (including pipeline_last)
20641f5dfcbSCristian Dumitrescu  *       exactly one time during the current dispatch loop iteration. This takes place when the DP
20741f5dfcbSCristian Dumitrescu  *       thread sees the final value of t->n_pipelines;
20841f5dfcbSCristian Dumitrescu  *     - Not run the removed pipeline at all, run all the other pipelines, except pipeline_last,
20941f5dfcbSCristian Dumitrescu  *       exactly one time and the pipeline_last exactly two times during the current dispatch loop
21041f5dfcbSCristian Dumitrescu  *       iteration. This takes place when the DP thread sees the initial value of t->n_pipelines.
21141f5dfcbSCristian Dumitrescu  *  - If the memory barrier below is not present, then the t->n_pipelines update may be reordered by
21241f5dfcbSCristian Dumitrescu  *    the CPU, so that it takes place before the t->pipelines[] update. The possible DP thread
21341f5dfcbSCristian Dumitrescu  *    behaviors are detailed below, which are all valid:
21441f5dfcbSCristian Dumitrescu  *     - Not run the removed pipeline at all, run all the other pipelines (including pipeline_last)
21541f5dfcbSCristian Dumitrescu  *       exactly one time during the current dispatch loop iteration. This takes place when the DP
21641f5dfcbSCristian Dumitrescu  *       thread sees the final values of the t->pipeline[] array;
21741f5dfcbSCristian Dumitrescu  *     - Run the removed pipeline one last time, run all the other pipelines exactly one time, with
21841f5dfcbSCristian Dumitrescu  *       the exception of the pipeline_last, which is not run during the current dispatch loop
21941f5dfcbSCristian Dumitrescu  *       iteration. This takes place when the DP thread sees the initial values of t->pipeline[].
22041f5dfcbSCristian Dumitrescu  *
22141f5dfcbSCristian Dumitrescu  * DP thread:
22241f5dfcbSCristian Dumitrescu  *  - Reads t->n_pipelines before starting every new iteration through t->pipelines[].
223b77f6600SCristian Dumitrescu  */
22441f5dfcbSCristian Dumitrescu void
pipeline_disable(struct rte_swx_pipeline * p)22541f5dfcbSCristian Dumitrescu pipeline_disable(struct rte_swx_pipeline *p)
226b77f6600SCristian Dumitrescu {
22741f5dfcbSCristian Dumitrescu 	struct thread *t;
22841f5dfcbSCristian Dumitrescu 	uint64_t n_pipelines;
22941f5dfcbSCristian Dumitrescu 	uint32_t thread_id, i;
230b77f6600SCristian Dumitrescu 
23141f5dfcbSCristian Dumitrescu 	/* Check input params */
23241f5dfcbSCristian Dumitrescu 	if (!p)
23341f5dfcbSCristian Dumitrescu 		return;
234b77f6600SCristian Dumitrescu 
23541f5dfcbSCristian Dumitrescu 	/* Find the thread that runs this pipeline. */
23641f5dfcbSCristian Dumitrescu 	thread_id = pipeline_find(p);
23741f5dfcbSCristian Dumitrescu 	if (thread_id == RTE_MAX_LCORE)
23841f5dfcbSCristian Dumitrescu 		return;
239b77f6600SCristian Dumitrescu 
24041f5dfcbSCristian Dumitrescu 	t = &threads[thread_id];
24141f5dfcbSCristian Dumitrescu 	n_pipelines = t->n_pipelines;
242b77f6600SCristian Dumitrescu 
243b77f6600SCristian Dumitrescu 	for (i = 0; i < n_pipelines; i++) {
24441f5dfcbSCristian Dumitrescu 		struct rte_swx_pipeline *pipeline = t->pipelines[i];
245b77f6600SCristian Dumitrescu 
24641f5dfcbSCristian Dumitrescu 		if (pipeline != p)
247b77f6600SCristian Dumitrescu 			continue;
248b77f6600SCristian Dumitrescu 
249b77f6600SCristian Dumitrescu 		if (i < n_pipelines - 1) {
25041f5dfcbSCristian Dumitrescu 			struct rte_swx_pipeline *pipeline_last = t->pipelines[n_pipelines - 1];
251b77f6600SCristian Dumitrescu 
25241f5dfcbSCristian Dumitrescu 			t->pipelines[i] = pipeline_last;
253b77f6600SCristian Dumitrescu 		}
254b77f6600SCristian Dumitrescu 
25541f5dfcbSCristian Dumitrescu 		rte_wmb();
25641f5dfcbSCristian Dumitrescu 		t->n_pipelines = n_pipelines - 1;
257b77f6600SCristian Dumitrescu 
25841f5dfcbSCristian Dumitrescu 		return;
259b77f6600SCristian Dumitrescu 	}
260b77f6600SCristian Dumitrescu 
26141f5dfcbSCristian Dumitrescu 	return;
262b77f6600SCristian Dumitrescu }
263b77f6600SCristian Dumitrescu 
264ce5d73a9SCristian Dumitrescu int
block_enable(block_run_f block_func,void * block,uint32_t thread_id)265ce5d73a9SCristian Dumitrescu block_enable(block_run_f block_func, void *block, uint32_t thread_id)
266ce5d73a9SCristian Dumitrescu {
267ce5d73a9SCristian Dumitrescu 	struct thread *t;
268ce5d73a9SCristian Dumitrescu 	uint64_t n_blocks;
269ce5d73a9SCristian Dumitrescu 
270ce5d73a9SCristian Dumitrescu 	/* Check input params */
271ce5d73a9SCristian Dumitrescu 	if (!block_func || !block || thread_id >= RTE_MAX_LCORE)
272ce5d73a9SCristian Dumitrescu 		return -EINVAL;
273ce5d73a9SCristian Dumitrescu 
274ce5d73a9SCristian Dumitrescu 	if (block_find(block) < RTE_MAX_LCORE)
275ce5d73a9SCristian Dumitrescu 		return -EEXIST;
276ce5d73a9SCristian Dumitrescu 
277ce5d73a9SCristian Dumitrescu 	t = &threads[thread_id];
278ce5d73a9SCristian Dumitrescu 	if (!t->enabled)
279ce5d73a9SCristian Dumitrescu 		return -EINVAL;
280ce5d73a9SCristian Dumitrescu 
281ce5d73a9SCristian Dumitrescu 	n_blocks = t->n_blocks;
282ce5d73a9SCristian Dumitrescu 
283ce5d73a9SCristian Dumitrescu 	/* Check there is room for at least one more block. */
284ce5d73a9SCristian Dumitrescu 	if (n_blocks >= THREAD_BLOCKS_MAX)
285ce5d73a9SCristian Dumitrescu 		return -ENOSPC;
286ce5d73a9SCristian Dumitrescu 
287ce5d73a9SCristian Dumitrescu 	/* Install the new block. */
288ce5d73a9SCristian Dumitrescu 	t->blocks[n_blocks]->block_func = block_func;
289ce5d73a9SCristian Dumitrescu 	t->blocks[n_blocks]->block = block;
290ce5d73a9SCristian Dumitrescu 
291ce5d73a9SCristian Dumitrescu 	rte_wmb();
292ce5d73a9SCristian Dumitrescu 	t->n_blocks = n_blocks + 1;
293ce5d73a9SCristian Dumitrescu 
294ce5d73a9SCristian Dumitrescu 	return 0;
295ce5d73a9SCristian Dumitrescu }
296ce5d73a9SCristian Dumitrescu 
297ce5d73a9SCristian Dumitrescu void
block_disable(void * block)298ce5d73a9SCristian Dumitrescu block_disable(void *block)
299ce5d73a9SCristian Dumitrescu {
300ce5d73a9SCristian Dumitrescu 	struct thread *t;
301ce5d73a9SCristian Dumitrescu 	uint64_t n_blocks;
302ce5d73a9SCristian Dumitrescu 	uint32_t thread_id, i;
303ce5d73a9SCristian Dumitrescu 
304ce5d73a9SCristian Dumitrescu 	/* Check input params */
305ce5d73a9SCristian Dumitrescu 	if (!block)
306ce5d73a9SCristian Dumitrescu 		return;
307ce5d73a9SCristian Dumitrescu 
308ce5d73a9SCristian Dumitrescu 	/* Find the thread that runs this block. */
309ce5d73a9SCristian Dumitrescu 	thread_id = block_find(block);
310ce5d73a9SCristian Dumitrescu 	if (thread_id == RTE_MAX_LCORE)
311ce5d73a9SCristian Dumitrescu 		return;
312ce5d73a9SCristian Dumitrescu 
313ce5d73a9SCristian Dumitrescu 	t = &threads[thread_id];
314ce5d73a9SCristian Dumitrescu 	n_blocks = t->n_blocks;
315ce5d73a9SCristian Dumitrescu 
316ce5d73a9SCristian Dumitrescu 	for (i = 0; i < n_blocks; i++) {
317ce5d73a9SCristian Dumitrescu 		struct block *b = t->blocks[i];
318ce5d73a9SCristian Dumitrescu 
319ce5d73a9SCristian Dumitrescu 		if (block != b->block)
320ce5d73a9SCristian Dumitrescu 			continue;
321ce5d73a9SCristian Dumitrescu 
322ce5d73a9SCristian Dumitrescu 		if (i < n_blocks - 1) {
323ce5d73a9SCristian Dumitrescu 			struct block *block_last = t->blocks[n_blocks - 1];
324ce5d73a9SCristian Dumitrescu 
325ce5d73a9SCristian Dumitrescu 			t->blocks[i] = block_last;
326ce5d73a9SCristian Dumitrescu 		}
327ce5d73a9SCristian Dumitrescu 
328ce5d73a9SCristian Dumitrescu 		rte_wmb();
329ce5d73a9SCristian Dumitrescu 		t->n_blocks = n_blocks - 1;
330ce5d73a9SCristian Dumitrescu 
331ce5d73a9SCristian Dumitrescu 		rte_wmb();
332ce5d73a9SCristian Dumitrescu 		t->blocks[n_blocks - 1] = b;
333ce5d73a9SCristian Dumitrescu 
334ce5d73a9SCristian Dumitrescu 		return;
335ce5d73a9SCristian Dumitrescu 	}
336ce5d73a9SCristian Dumitrescu }
337ce5d73a9SCristian Dumitrescu 
338b77f6600SCristian Dumitrescu /**
33941f5dfcbSCristian Dumitrescu  * Data plane (DP) threads.
34041f5dfcbSCristian Dumitrescu  *
341ce5d73a9SCristian Dumitrescu 
342ce5d73a9SCristian Dumitrescu 
34341f5dfcbSCristian Dumitrescu  * The t->n_pipelines variable is modified by the CP thread every time changes to the t->pipeline[]
34441f5dfcbSCristian Dumitrescu  * array are operated, so it is therefore very important that the latest value of t->n_pipelines is
34541f5dfcbSCristian Dumitrescu  * read by the DP thread at the beginning of every new dispatch loop iteration, otherwise a stale
34641f5dfcbSCristian Dumitrescu  * t->n_pipelines value may result in new pipelines not being detected, running pipelines that have
34741f5dfcbSCristian Dumitrescu  * been removed and are possibly no longer valid (e.g. when the pipeline_last is removed), running
34841f5dfcbSCristian Dumitrescu  * one pipeline (pipeline_last) twice as frequently than the rest of the pipelines (e.g. when a
34941f5dfcbSCristian Dumitrescu  * pipeline other than pipeline_last is removed), etc. This is the reason why t->n_pipelines is
35041f5dfcbSCristian Dumitrescu  * marked as volatile.
351b77f6600SCristian Dumitrescu  */
352b77f6600SCristian Dumitrescu int
thread_main(void * arg __rte_unused)353b77f6600SCristian Dumitrescu thread_main(void *arg __rte_unused)
354b77f6600SCristian Dumitrescu {
35541f5dfcbSCristian Dumitrescu 	struct thread *t;
35641f5dfcbSCristian Dumitrescu 	uint32_t thread_id;
357b77f6600SCristian Dumitrescu 
358b77f6600SCristian Dumitrescu 	thread_id = rte_lcore_id();
35941f5dfcbSCristian Dumitrescu 	t = &threads[thread_id];
360b77f6600SCristian Dumitrescu 
36141f5dfcbSCristian Dumitrescu 	/* Dispatch loop. */
36241f5dfcbSCristian Dumitrescu 	for ( ; ; ) {
36341f5dfcbSCristian Dumitrescu 		uint32_t i;
364b77f6600SCristian Dumitrescu 
36541f5dfcbSCristian Dumitrescu 		/* Pipelines. */
36641f5dfcbSCristian Dumitrescu 		for (i = 0; i < t->n_pipelines; i++)
36741f5dfcbSCristian Dumitrescu 			rte_swx_pipeline_run(t->pipelines[i], PIPELINE_INSTR_QUANTA);
368ce5d73a9SCristian Dumitrescu 
369ce5d73a9SCristian Dumitrescu 		/* Blocks. */
370ce5d73a9SCristian Dumitrescu 		for (i = 0; i < t->n_blocks; i++) {
371ce5d73a9SCristian Dumitrescu 			struct block *b = t->blocks[i];
372ce5d73a9SCristian Dumitrescu 
373ce5d73a9SCristian Dumitrescu 			b->block_func(b->block);
374ce5d73a9SCristian Dumitrescu 		}
375b77f6600SCristian Dumitrescu 	}
376b77f6600SCristian Dumitrescu 
377b77f6600SCristian Dumitrescu 	return 0;
378b77f6600SCristian Dumitrescu }
379