Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2021 Intel Corporation.
3 : : * All rights reserved.
4 : : */
5 : :
6 : : #include "spdk/stdinc.h"
7 : :
8 : : #include "spdk/env.h"
9 : : #include "spdk/event.h"
10 : : #include "spdk/likely.h"
11 : : #include "spdk/json.h"
12 : : #include "spdk/jsonrpc.h"
13 : : #include "spdk/rpc.h"
14 : : #include "spdk/string.h"
15 : : #include "spdk/thread.h"
16 : : #include "spdk/util.h"
17 : :
18 : : #include "spdk_internal/event.h"
19 : :
20 : : static bool g_is_running = true;
21 : : pthread_mutex_t g_sched_list_mutex = PTHREAD_MUTEX_INITIALIZER;
22 : : #define TIMESLICE_US 100 * 1000
23 : : static bool g_for_each_reactor = false;
24 : :
25 : : struct sched_thread {
26 : : struct spdk_thread *thread;
27 : : struct spdk_poller *poller;
28 : : struct spdk_poller *idle_poller;
29 : : int active_percent;
30 : : struct spdk_jsonrpc_request *request;
31 : : TAILQ_ENTRY(sched_thread) link;
32 : : };
33 : :
34 : : static TAILQ_HEAD(, sched_thread) g_sched_threads = TAILQ_HEAD_INITIALIZER(g_sched_threads);
35 : :
36 : : struct rpc_thread_create {
37 : : int active_percent;
38 : : char *name;
39 : : char *cpu_mask;
40 : : };
41 : :
42 : : static void
43 : 234 : free_rpc_thread_create(struct rpc_thread_create *req)
44 : : {
45 : 234 : free(req->name);
46 : 234 : free(req->cpu_mask);
47 : 234 : }
48 : :
49 : : static const struct spdk_json_object_decoder rpc_thread_create_decoders[] = {
50 : : {"active", offsetof(struct rpc_thread_create, active_percent), spdk_json_decode_uint64},
51 : : {"name", offsetof(struct rpc_thread_create, name), spdk_json_decode_string, true},
52 : : {"cpu_mask", offsetof(struct rpc_thread_create, cpu_mask), spdk_json_decode_string, true},
53 : : };
54 : :
55 : : static void
56 : 234 : rpc_scheduler_thread_create_cb(struct spdk_jsonrpc_request *request, uint64_t thread_id)
57 : : {
58 : : struct spdk_json_write_ctx *w;
59 : :
60 : 234 : w = spdk_jsonrpc_begin_result(request);
61 : 234 : spdk_json_write_uint64(w, thread_id);
62 : 234 : spdk_jsonrpc_end_result(request, w);
63 : 234 : }
64 : :
65 : : static void
66 : 234 : thread_delete(struct sched_thread *sched_thread)
67 : : {
68 : 234 : spdk_poller_unregister(&sched_thread->poller);
69 : 234 : spdk_poller_unregister(&sched_thread->idle_poller);
70 : 234 : spdk_thread_exit(sched_thread->thread);
71 : :
72 [ + + ]: 234 : TAILQ_REMOVE(&g_sched_threads, sched_thread, link);
73 : 234 : free(sched_thread);
74 : :
75 [ + + + + : 234 : if (!g_is_running && TAILQ_EMPTY(&g_sched_threads)) {
+ + ]
76 : 21 : spdk_app_stop(0);
77 : : }
78 : 234 : }
79 : :
80 : : static int
81 : 1753 : poller_run_busy(void *arg)
82 : : {
83 : 1753 : struct sched_thread *sched_thread = arg;
84 : :
85 [ - + - + ]: 1753 : if (spdk_unlikely(!g_is_running)) {
86 : 0 : pthread_mutex_lock(&g_sched_list_mutex);
87 : 0 : thread_delete(sched_thread);
88 : 0 : pthread_mutex_unlock(&g_sched_list_mutex);
89 : 0 : return SPDK_POLLER_IDLE;
90 : : }
91 : :
92 : 1753 : spdk_delay_us(TIMESLICE_US * sched_thread->active_percent / 100);
93 : 1753 : return SPDK_POLLER_BUSY;
94 : : }
95 : :
96 : : static int
97 : 1929461 : poller_run_idle(void *arg)
98 : : {
99 : 1929461 : struct sched_thread *sched_thread = arg;
100 : :
101 [ + + + + ]: 1929461 : if (spdk_unlikely(!g_is_running)) {
102 : 210 : pthread_mutex_lock(&g_sched_list_mutex);
103 : 210 : thread_delete(sched_thread);
104 : 210 : pthread_mutex_unlock(&g_sched_list_mutex);
105 : 210 : return SPDK_POLLER_IDLE;
106 : : }
107 : :
108 : 1929251 : spdk_delay_us(10);
109 : 1929251 : return SPDK_POLLER_IDLE;
110 : : }
111 : :
112 : : static void
113 : 258 : update_pollers(struct sched_thread *sched_thread)
114 : : {
115 : 258 : spdk_poller_unregister(&sched_thread->poller);
116 [ + + ]: 258 : if (sched_thread->active_percent > 0) {
117 : 150 : sched_thread->poller = spdk_poller_register_named(poller_run_busy, sched_thread, TIMESLICE_US,
118 : 150 : spdk_thread_get_name(sched_thread->thread));
119 [ - + ]: 150 : assert(sched_thread->poller != NULL);
120 : : }
121 [ + + ]: 258 : if (sched_thread->idle_poller == NULL) {
122 : 234 : sched_thread->idle_poller = spdk_poller_register_named(poller_run_idle, sched_thread, 0,
123 : : "idle_poller");
124 [ - + ]: 234 : assert(sched_thread->idle_poller != NULL);
125 : : }
126 : 258 : }
127 : :
128 : : static void
129 : 234 : rpc_register_poller(void *arg)
130 : : {
131 : 234 : struct sched_thread *sched_thread = arg;
132 : :
133 : 234 : update_pollers(sched_thread);
134 : :
135 [ + - ]: 234 : if (sched_thread->request != NULL) {
136 : 234 : rpc_scheduler_thread_create_cb(sched_thread->request, spdk_thread_get_id(sched_thread->thread));
137 : 234 : sched_thread->request = NULL;
138 : : }
139 : 234 : }
140 : :
141 : : static void
142 : 234 : rpc_scheduler_thread_create(struct spdk_jsonrpc_request *request,
143 : : const struct spdk_json_val *params)
144 : : {
145 : : struct sched_thread *sched_thread;
146 : 234 : struct rpc_thread_create req = {0};
147 : 234 : struct spdk_cpuset *cpu_set = NULL;
148 : 234 : int rc = 0;
149 : :
150 [ - + ]: 234 : if (spdk_json_decode_object(params, rpc_thread_create_decoders,
151 : : SPDK_COUNTOF(rpc_thread_create_decoders),
152 : : &req)) {
153 : 0 : spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
154 : : "Invalid parameters provided");
155 : 0 : return;
156 : : }
157 : :
158 [ + - - + ]: 234 : if (req.active_percent < 0 || req.active_percent > 100) {
159 : 0 : SPDK_ERRLOG("invalid percent value %d\n", req.active_percent);
160 : 0 : spdk_jsonrpc_send_error_response(request, -EINVAL, spdk_strerror(EINVAL));
161 : 0 : free_rpc_thread_create(&req);
162 : 0 : return;
163 : : }
164 : :
165 [ + + ]: 234 : if (req.cpu_mask != NULL) {
166 : 171 : cpu_set = calloc(1, sizeof(*cpu_set));
167 [ - + ]: 171 : assert(cpu_set != NULL);
168 : 171 : rc = spdk_cpuset_parse(cpu_set, req.cpu_mask);
169 [ - + ]: 171 : if (rc < 0) {
170 : 0 : SPDK_ERRLOG("invalid cpumask %s\n", req.cpu_mask);
171 : 0 : spdk_jsonrpc_send_error_response(request, -EINVAL, spdk_strerror(EINVAL));
172 : 0 : free_rpc_thread_create(&req);
173 : 0 : free(cpu_set);
174 : 0 : return;
175 : : }
176 : : }
177 : :
178 : 234 : sched_thread = calloc(1, sizeof(*sched_thread));
179 [ - + ]: 234 : assert(sched_thread != NULL);
180 : :
181 : 234 : sched_thread->thread = spdk_thread_create(req.name, cpu_set);
182 [ - + ]: 234 : assert(sched_thread->thread != NULL);
183 : 234 : free(cpu_set);
184 : :
185 : 234 : sched_thread->request = request;
186 : 234 : sched_thread->active_percent = req.active_percent;
187 : :
188 : 234 : spdk_thread_send_msg(sched_thread->thread, rpc_register_poller, sched_thread);
189 : :
190 : 234 : free_rpc_thread_create(&req);
191 : :
192 [ - + ]: 234 : pthread_mutex_lock(&g_sched_list_mutex);
193 : 234 : TAILQ_INSERT_TAIL(&g_sched_threads, sched_thread, link);
194 [ - + ]: 234 : pthread_mutex_unlock(&g_sched_list_mutex);
195 : :
196 : 234 : return;
197 : : }
198 : :
199 : 22 : SPDK_RPC_REGISTER("scheduler_thread_create", rpc_scheduler_thread_create, SPDK_RPC_RUNTIME)
200 : :
201 : : struct rpc_thread_set_active_ctx {
202 : : int active_percent;
203 : : struct spdk_jsonrpc_request *request;
204 : : };
205 : :
206 : : struct rpc_thread_set_active {
207 : : uint64_t thread_id;
208 : : int active_percent;
209 : : };
210 : :
211 : : static const struct spdk_json_object_decoder rpc_thread_set_active_decoders[] = {
212 : : {"thread_id", offsetof(struct rpc_thread_set_active, thread_id), spdk_json_decode_uint64},
213 : : {"active", offsetof(struct rpc_thread_set_active, active_percent), spdk_json_decode_uint64},
214 : : };
215 : :
216 : : static void
217 : 24 : rpc_scheduler_thread_set_active_cb(void *arg)
218 : : {
219 : 24 : struct rpc_thread_set_active_ctx *ctx = arg;
220 : : uint64_t thread_id;
221 : : struct sched_thread *sched_thread;
222 : :
223 : 24 : thread_id = spdk_thread_get_id(spdk_get_thread());
224 : :
225 [ - + ]: 24 : pthread_mutex_lock(&g_sched_list_mutex);
226 [ + - ]: 216 : TAILQ_FOREACH(sched_thread, &g_sched_threads, link) {
227 [ + + ]: 216 : if (spdk_thread_get_id(sched_thread->thread) == thread_id) {
228 : 24 : sched_thread->active_percent = ctx->active_percent;
229 : 24 : update_pollers(sched_thread);
230 [ - + ]: 24 : pthread_mutex_unlock(&g_sched_list_mutex);
231 : 24 : spdk_jsonrpc_send_bool_response(ctx->request, true);
232 : 24 : free(ctx);
233 : 24 : return;
234 : : }
235 : : }
236 [ # # ]: 0 : pthread_mutex_unlock(&g_sched_list_mutex);
237 : :
238 : 0 : spdk_jsonrpc_send_error_response(ctx->request, -ENOENT, spdk_strerror(ENOENT));
239 : 0 : free(ctx);
240 : 0 : return;
241 : : }
242 : :
243 : : static void
244 : 24 : rpc_scheduler_thread_set_active(struct spdk_jsonrpc_request *request,
245 : : const struct spdk_json_val *params)
246 : : {
247 : : struct spdk_thread *thread;
248 : 24 : struct rpc_thread_set_active req = {0};
249 : : struct rpc_thread_set_active_ctx *ctx;
250 : :
251 [ - + ]: 24 : if (spdk_json_decode_object(params, rpc_thread_set_active_decoders,
252 : : SPDK_COUNTOF(rpc_thread_set_active_decoders),
253 : : &req)) {
254 : 0 : spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
255 : : "Invalid parameters provided");
256 : 0 : return;
257 : : }
258 : :
259 [ + - - + ]: 24 : if (req.active_percent < 0 || req.active_percent > 100) {
260 : 0 : SPDK_ERRLOG("invalid percent value %d\n", req.active_percent);
261 : 0 : spdk_jsonrpc_send_error_response(request, -EINVAL, spdk_strerror(EINVAL));
262 : 0 : return;
263 : : }
264 : :
265 : 24 : thread = spdk_thread_get_by_id(req.thread_id);
266 [ - + ]: 24 : if (thread == NULL) {
267 : 0 : spdk_jsonrpc_send_error_response(request, -ENOENT, spdk_strerror(ENOENT));
268 : 0 : return;
269 : : }
270 : :
271 : 24 : ctx = calloc(1, sizeof(*ctx));
272 [ - + ]: 24 : if (ctx == NULL) {
273 : 0 : spdk_jsonrpc_send_error_response(request, -ENOMEM, spdk_strerror(-ENOMEM));
274 : 0 : return;
275 : : }
276 : 24 : ctx->request = request;
277 : 24 : ctx->active_percent = req.active_percent;
278 : :
279 : 24 : spdk_thread_send_msg(thread, rpc_scheduler_thread_set_active_cb, ctx);
280 : : }
281 : :
282 : 22 : SPDK_RPC_REGISTER("scheduler_thread_set_active", rpc_scheduler_thread_set_active, SPDK_RPC_RUNTIME)
283 : :
284 : : struct rpc_thread_delete_ctx {
285 : : struct spdk_jsonrpc_request *request;
286 : : };
287 : :
288 : : struct rpc_thread_delete {
289 : : uint64_t thread_id;
290 : : };
291 : :
292 : : static const struct spdk_json_object_decoder rpc_thread_delete_decoders[] = {
293 : : {"thread_id", offsetof(struct rpc_thread_delete, thread_id), spdk_json_decode_uint64},
294 : : };
295 : :
296 : : static void
297 : 24 : rpc_scheduler_thread_delete_cb(void *arg)
298 : : {
299 : 24 : struct rpc_thread_delete_ctx *ctx = arg;
300 : : struct sched_thread *sched_thread;
301 : : uint64_t thread_id;
302 : :
303 : 24 : thread_id = spdk_thread_get_id(spdk_get_thread());
304 : :
305 [ - + ]: 24 : pthread_mutex_lock(&g_sched_list_mutex);
306 [ + - ]: 234 : TAILQ_FOREACH(sched_thread, &g_sched_threads, link) {
307 [ + + ]: 234 : if (spdk_thread_get_id(sched_thread->thread) == thread_id) {
308 : 24 : thread_delete(sched_thread);
309 [ - + ]: 24 : pthread_mutex_unlock(&g_sched_list_mutex);
310 : 24 : spdk_jsonrpc_send_bool_response(ctx->request, true);
311 : 24 : free(ctx);
312 : 24 : return;
313 : : }
314 : : }
315 [ # # ]: 0 : pthread_mutex_unlock(&g_sched_list_mutex);
316 : :
317 : 0 : spdk_jsonrpc_send_error_response(ctx->request, -ENOENT, spdk_strerror(ENOENT));
318 : 0 : free(ctx);
319 : 0 : return;
320 : : }
321 : :
322 : : static void
323 : 24 : rpc_scheduler_thread_delete(struct spdk_jsonrpc_request *request,
324 : : const struct spdk_json_val *params)
325 : : {
326 : : struct spdk_thread *thread;
327 : 24 : struct rpc_thread_delete req = {0};
328 : : struct rpc_thread_delete_ctx *ctx;
329 : :
330 [ - + ]: 24 : if (spdk_json_decode_object(params, rpc_thread_delete_decoders,
331 : : SPDK_COUNTOF(rpc_thread_delete_decoders),
332 : : &req)) {
333 : 0 : spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
334 : : "Invalid parameters provided");
335 : 0 : return;
336 : : }
337 : :
338 : 24 : thread = spdk_thread_get_by_id(req.thread_id);
339 [ - + ]: 24 : if (thread == NULL) {
340 : 0 : spdk_jsonrpc_send_error_response(request, -ENOENT, spdk_strerror(ENOENT));
341 : 0 : return;
342 : : }
343 : :
344 : 24 : ctx = calloc(1, sizeof(*ctx));
345 [ - + ]: 24 : if (ctx == NULL) {
346 : 0 : spdk_jsonrpc_send_error_response(request, -ENOMEM, spdk_strerror(-ENOMEM));
347 : 0 : return;
348 : : }
349 : 24 : ctx->request = request;
350 : :
351 : 24 : spdk_thread_send_msg(thread, rpc_scheduler_thread_delete_cb, ctx);
352 : : }
353 : :
354 : 22 : SPDK_RPC_REGISTER("scheduler_thread_delete", rpc_scheduler_thread_delete, SPDK_RPC_RUNTIME)
355 : :
356 : : static void
357 : 22 : test_shutdown(void)
358 : : {
359 : 22 : g_is_running = false;
360 : 22 : SPDK_NOTICELOG("Scheduler test application stopped.\n");
361 [ - + ]: 22 : pthread_mutex_lock(&g_sched_list_mutex);
362 [ + + ]: 22 : if (TAILQ_EMPTY(&g_sched_threads)) {
363 : 1 : spdk_app_stop(0);
364 : : }
365 [ - + ]: 22 : pthread_mutex_unlock(&g_sched_list_mutex);
366 : 22 : }
367 : :
368 : : static void
369 : 87272 : for_each_nop(void *arg1, void *arg2)
370 : : {
371 : 87272 : }
372 : :
373 : : static void
374 : 21839 : for_each_reactor_start(void *arg1, void *arg2)
375 : : {
376 : 21839 : spdk_for_each_reactor(for_each_nop, NULL, NULL, for_each_reactor_start);
377 : 21839 : }
378 : :
379 : : static void
380 : 22 : test_start(void *arg1)
381 : : {
382 : 22 : SPDK_NOTICELOG("Scheduler test application started.\n");
383 : : /* Start an spdk_for_each_reactor operation that just keeps
384 : : * running over and over again until the app exits. This
385 : : * serves as a regression test for SPDK issue #2206, ensuring
386 : : * that any pending spdk_for_each_reactor operations are
387 : : * completed before reactors are shut down.
388 : : */
389 [ + + + + ]: 22 : if (g_for_each_reactor) {
390 : 21 : for_each_reactor_start(NULL, NULL);
391 : : }
392 : 22 : }
393 : :
394 : : static void
395 : 0 : scheduler_usage(void)
396 : : {
397 [ # # ]: 0 : printf(" -f Enable spdk_for_each_reactor regression test\n");
398 : 0 : }
399 : :
400 : : static int
401 : 21 : scheduler_parse_arg(int ch, char *arg)
402 : : {
403 [ + - ]: 21 : switch (ch) {
404 : 21 : case 'f':
405 : 21 : g_for_each_reactor = true;
406 : 21 : break;
407 : 0 : default:
408 : 0 : return -EINVAL;
409 : : }
410 : 21 : return 0;
411 : : }
412 : :
413 : : int
414 : 22 : main(int argc, char **argv)
415 : : {
416 : 10 : struct spdk_app_opts opts;
417 : 22 : int rc = 0;
418 : :
419 : 22 : spdk_app_opts_init(&opts, sizeof(opts));
420 : 22 : opts.name = "scheduler";
421 : 22 : opts.shutdown_cb = test_shutdown;
422 : :
423 [ - + ]: 22 : if ((rc = spdk_app_parse_args(argc, argv, &opts,
424 : : "f", NULL, scheduler_parse_arg, scheduler_usage)) != SPDK_APP_PARSE_ARGS_SUCCESS) {
425 : 0 : return rc;
426 : : }
427 : :
428 : 22 : rc = spdk_app_start(&opts, test_start, NULL);
429 : :
430 : 22 : spdk_app_fini();
431 : :
432 : 22 : return rc;
433 : : }
|