xref: /dpdk/examples/service_cores/main.c (revision 328720c594f235aa44c78fe360e8e6f31b5e989f)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2017 Intel Corporation
3  */
4 
5 #include <unistd.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <stdint.h>
9 #include <errno.h>
10 #include <sys/queue.h>
11 
12 #include <rte_memory.h>
13 #include <rte_launch.h>
14 #include <rte_eal.h>
15 #include <rte_debug.h>
16 #include <rte_cycles.h>
17 
18 /* allow application scheduling of the services */
19 #include <rte_service.h>
20 
21 /* Allow application registration of its own services. An application does not
22  * have to register services, but it can be useful if it wishes to run a
23  * function on a core that is otherwise in use as a service core. In this
24  * example, all services are dummy services registered by the sample app itself.
25  */
26 #include <rte_service_component.h>
27 
28 #define PROFILE_CORES_MAX 8
29 #define PROFILE_SERVICE_PER_CORE 5
30 
31 /* dummy function to do "work" */
service_func(void * args)32 static int32_t service_func(void *args)
33 {
34 	RTE_SET_USED(args);
35 	rte_delay_us(2000);
36 	return 0;
37 }
38 
39 static struct rte_service_spec services[] = {
40 	{"service_1", service_func, NULL, 0, 0},
41 	{"service_2", service_func, NULL, 0, 0},
42 	{"service_3", service_func, NULL, 0, 0},
43 	{"service_4", service_func, NULL, 0, 0},
44 	{"service_5", service_func, NULL, 0, 0},
45 };
46 #define NUM_SERVICES RTE_DIM(services)
47 
48 /* this struct holds the mapping of a particular core to all services */
49 struct profile_for_core {
50 	uint32_t mapped_services[PROFILE_SERVICE_PER_CORE];
51 };
52 
53 /* struct that can be applied as the service core mapping. Items in this
54  * struct will be passed to the ordinary rte_service_* APIs to configure the
55  * service cores at runtime, based on the requirements.
56  *
57  * These profiles can be considered a "configuration" for the service cores,
58  * where switching profile just changes the number of cores and the mappings
59  * for each of them. As a result, the core requirements and performance of the
60  * application scales.
61  */
62 struct profile {
63 	char name[64];
64 	uint32_t num_cores;
65 	struct profile_for_core cores[PROFILE_CORES_MAX];
66 };
67 
68 static struct profile profiles[] = {
69 	/* profile 0: high performance */
70 	{
71 		.name = "High Performance",
72 		.num_cores = 5,
73 		.cores[0] = {.mapped_services = {1, 0, 0, 0, 0} },
74 		.cores[1] = {.mapped_services = {0, 1, 0, 0, 0} },
75 		.cores[2] = {.mapped_services = {0, 0, 1, 0, 0} },
76 		.cores[3] = {.mapped_services = {0, 0, 0, 1, 0} },
77 		.cores[4] = {.mapped_services = {0, 0, 0, 0, 1} },
78 	},
79 	/* profile 1: mid performance with single service priority */
80 	{
81 		.name = "Mid-High Performance",
82 		.num_cores = 3,
83 		.cores[0] = {.mapped_services = {1, 1, 0, 0, 0} },
84 		.cores[1] = {.mapped_services = {0, 0, 1, 1, 0} },
85 		.cores[2] = {.mapped_services = {0, 0, 0, 0, 1} },
86 		.cores[3] = {.mapped_services = {0, 0, 0, 0, 0} },
87 		.cores[4] = {.mapped_services = {0, 0, 0, 0, 0} },
88 	},
89 	/* profile 2: mid performance with single service priority */
90 	{
91 		.name = "Mid-Low Performance",
92 		.num_cores = 2,
93 		.cores[0] = {.mapped_services = {1, 1, 1, 0, 0} },
94 		.cores[1] = {.mapped_services = {1, 1, 0, 1, 1} },
95 		.cores[2] = {.mapped_services = {0, 0, 0, 0, 0} },
96 		.cores[3] = {.mapped_services = {0, 0, 0, 0, 0} },
97 		.cores[4] = {.mapped_services = {0, 0, 0, 0, 0} },
98 	},
99 	/* profile 3: scale down performance on single core */
100 	{
101 		.name = "Scale down performance",
102 		.num_cores = 1,
103 		.cores[0] = {.mapped_services = {1, 1, 1, 1, 1} },
104 		.cores[1] = {.mapped_services = {0, 0, 0, 0, 0} },
105 		.cores[2] = {.mapped_services = {0, 0, 0, 0, 0} },
106 		.cores[3] = {.mapped_services = {0, 0, 0, 0, 0} },
107 		.cores[4] = {.mapped_services = {0, 0, 0, 0, 0} },
108 	},
109 };
110 #define NUM_PROFILES RTE_DIM(profiles)
111 
112 static void
apply_profile(int profile_id)113 apply_profile(int profile_id)
114 {
115 	uint32_t i;
116 	uint32_t s;
117 	int ret;
118 	struct profile *p = &profiles[profile_id];
119 	const uint8_t core_off = 1;
120 
121 	if (p->num_cores > rte_lcore_count() - 1) {
122 		printf("insufficent cores to run (%s)",
123 			p->name);
124 		return;
125 	}
126 
127 	for (i = 0; i < p->num_cores; i++) {
128 		uint32_t core = i + core_off;
129 		ret = rte_service_lcore_add(core);
130 		if (ret && ret != -EALREADY)
131 			printf("core %d added ret %d\n", core, ret);
132 
133 		ret = rte_service_lcore_start(core);
134 		if (ret && ret != -EALREADY)
135 			printf("core %d start ret %d\n", core, ret);
136 
137 		for (s = 0; s < NUM_SERVICES; s++) {
138 			if (rte_service_map_lcore_set(s, core,
139 					p->cores[i].mapped_services[s]))
140 				printf("failed to map lcore %d\n", core);
141 		}
142 	}
143 
144 	for ( ; i < PROFILE_CORES_MAX; i++) {
145 		uint32_t core = i + core_off;
146 		for (s = 0; s < NUM_SERVICES; s++) {
147 			ret = rte_service_map_lcore_set(s, core, 0);
148 			if (ret && ret != -EINVAL) {
149 				printf("%s %d: map lcore set = %d\n", __func__,
150 						__LINE__, ret);
151 			}
152 		}
153 		ret = rte_service_lcore_stop(core);
154 		if (ret && ret != -EALREADY) {
155 			printf("%s %d: lcore stop = %d\n", __func__,
156 					__LINE__, ret);
157 		}
158 		ret = rte_service_lcore_del(core);
159 		if (ret && ret != -EINVAL) {
160 			printf("%s %d: lcore del = %d\n", __func__,
161 					__LINE__, ret);
162 		}
163 	}
164 }
165 
166 int
main(int argc,char ** argv)167 main(int argc, char **argv)
168 {
169 	int ret;
170 
171 	ret = rte_eal_init(argc, argv);
172 	if (ret < 0)
173 		rte_panic("Cannot init EAL\n");
174 
175 	uint32_t i;
176 	for (i = 0; i < NUM_SERVICES; i++) {
177 		services[i].callback_userdata = 0;
178 		uint32_t id;
179 		/* Register a service as an application. 8< */
180 		ret = rte_service_component_register(&services[i], &id);
181 		if (ret)
182 			rte_exit(-1, "service register() failed");
183 
184 		/* set the service itself to be ready to run. In the case of
185 		 * ethdev, eventdev etc PMDs, this will be set when the
186 		 * appropriate configure or setup function is called.
187 		 */
188 		rte_service_component_runstate_set(id, 1);
189 
190 		/* Collect statistics for the service */
191 		rte_service_set_stats_enable(id, 1);
192 
193 		/* the application sets the service to be active. Note that the
194 		 * previous component_runstate_set() is the PMD indicating
195 		 * ready, while this function is the application setting the
196 		 * service to run. Applications can choose to not run a service
197 		 * by setting runstate to 0 at any time.
198 		 */
199 		ret = rte_service_runstate_set(id, 1);
200 		if (ret)
201 			return -ENOEXEC;
202 		/* >8 End of registering a service as an application. */
203 	}
204 
205 	i = 0;
206 	while (1) {
207 		const char clr[] = { 27, '[', '2', 'J', '\0' };
208 		const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' };
209 		printf("%s%s", clr, topLeft);
210 
211 		apply_profile(i);
212 		printf("\n==> Profile: %s\n\n", profiles[i].name);
213 
214 		rte_delay_us_sleep(1 * US_PER_S);
215 		rte_service_dump(stdout, UINT32_MAX);
216 
217 		rte_delay_us_sleep(5 * US_PER_S);
218 		rte_service_dump(stdout, UINT32_MAX);
219 
220 		i++;
221 		if (i >= NUM_PROFILES)
222 			i = 0;
223 	}
224 
225 	/* clean up the EAL */
226 	rte_eal_cleanup();
227 
228 	return 0;
229 }
230