Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2016 Intel Corporation.
3 : : * All rights reserved.
4 : : */
5 : :
6 : : #include "spdk/stdinc.h"
7 : : #include "spdk/likely.h"
8 : :
9 : : #include "spdk_internal/event.h"
10 : : #include "spdk_internal/usdt.h"
11 : :
12 : : #include "spdk/log.h"
13 : : #include "spdk/thread.h"
14 : : #include "spdk/env.h"
15 : : #include "spdk/util.h"
16 : : #include "spdk/scheduler.h"
17 : : #include "spdk/string.h"
18 : : #include "spdk/fd_group.h"
19 : :
20 : : #ifdef __linux__
21 : : #include <sys/prctl.h>
22 : : #include <sys/eventfd.h>
23 : : #endif
24 : :
25 : : #ifdef __FreeBSD__
26 : : #include <pthread_np.h>
27 : : #endif
28 : :
29 : : #define SPDK_EVENT_BATCH_SIZE 8
30 : :
31 : : static struct spdk_reactor *g_reactors;
32 : : static uint32_t g_reactor_count;
33 : : static struct spdk_cpuset g_reactor_core_mask;
34 : : static enum spdk_reactor_state g_reactor_state = SPDK_REACTOR_STATE_UNINITIALIZED;
35 : :
36 : : static bool g_framework_context_switch_monitor_enabled = true;
37 : :
38 : : static struct spdk_mempool *g_spdk_event_mempool = NULL;
39 : :
40 : : TAILQ_HEAD(, spdk_scheduler) g_scheduler_list
41 : : = TAILQ_HEAD_INITIALIZER(g_scheduler_list);
42 : :
43 : : static struct spdk_scheduler *g_scheduler = NULL;
44 : : static struct spdk_reactor *g_scheduling_reactor;
45 : : bool g_scheduling_in_progress = false;
46 : : static uint64_t g_scheduler_period = 0;
47 : : static uint32_t g_scheduler_core_number;
48 : : static struct spdk_scheduler_core_info *g_core_infos = NULL;
49 : :
50 : : TAILQ_HEAD(, spdk_governor) g_governor_list
51 : : = TAILQ_HEAD_INITIALIZER(g_governor_list);
52 : :
53 : : static struct spdk_governor *g_governor = NULL;
54 : :
55 : : static int reactor_interrupt_init(struct spdk_reactor *reactor);
56 : : static void reactor_interrupt_fini(struct spdk_reactor *reactor);
57 : :
58 : : static pthread_mutex_t g_stopping_reactors_mtx = PTHREAD_MUTEX_INITIALIZER;
59 : : static bool g_stopping_reactors = false;
60 : :
61 : : static struct spdk_scheduler *
62 : 8523 : _scheduler_find(const char *name)
63 : : {
64 : : struct spdk_scheduler *tmp;
65 : :
66 [ + + ]: 15757 : TAILQ_FOREACH(tmp, &g_scheduler_list, link) {
67 [ + + - + : 8130 : if (strcmp(name, tmp->name) == 0) {
+ + ]
68 : 896 : return tmp;
69 : : }
70 : : }
71 : :
72 : 7627 : return NULL;
73 : : }
74 : :
75 : : int
76 : 1779 : spdk_scheduler_set(const char *name)
77 : : {
78 : : struct spdk_scheduler *scheduler;
79 : 1779 : int rc = 0;
80 : :
81 : : /* NULL scheduler was specifically requested */
82 [ + + ]: 1779 : if (name == NULL) {
83 [ + - ]: 883 : if (g_scheduler) {
84 : 883 : g_scheduler->deinit();
85 : : }
86 : 883 : g_scheduler = NULL;
87 : 883 : return 0;
88 : : }
89 : :
90 : 896 : scheduler = _scheduler_find(name);
91 [ - + ]: 896 : if (scheduler == NULL) {
92 : 0 : SPDK_ERRLOG("Requested scheduler is missing\n");
93 : 0 : return -EINVAL;
94 : : }
95 : :
96 [ + + ]: 896 : if (g_scheduler == scheduler) {
97 : 9 : return 0;
98 : : }
99 : :
100 [ - + ]: 887 : if (g_scheduler) {
101 : 0 : g_scheduler->deinit();
102 : : }
103 : :
104 : 887 : rc = scheduler->init();
105 [ + - ]: 887 : if (rc == 0) {
106 : 887 : g_scheduler = scheduler;
107 : : } else {
108 : : /* Could not switch to the new scheduler, so keep the old
109 : : * one. We need to check if it wasn't NULL, and ->init() it again.
110 : : */
111 [ # # ]: 0 : if (g_scheduler) {
112 : 0 : SPDK_ERRLOG("Could not ->init() '%s' scheduler, reverting to '%s'\n",
113 : : name, g_scheduler->name);
114 : 0 : g_scheduler->init();
115 : : } else {
116 : 0 : SPDK_ERRLOG("Could not ->init() '%s' scheduler.\n", name);
117 : : }
118 : : }
119 : :
120 : 887 : return rc;
121 : : }
122 : :
123 : : struct spdk_scheduler *
124 : 1350 : spdk_scheduler_get(void)
125 : : {
126 : 1350 : return g_scheduler;
127 : : }
128 : :
129 : : uint64_t
130 : 180 : spdk_scheduler_get_period(void)
131 : : {
132 : : /* Convert from ticks to microseconds */
133 [ - + ]: 180 : return (g_scheduler_period * SPDK_SEC_TO_USEC / spdk_get_ticks_hz());
134 : : }
135 : :
136 : : void
137 : 1809 : spdk_scheduler_set_period(uint64_t period)
138 : : {
139 : : /* Convert microseconds to ticks */
140 : 1809 : g_scheduler_period = period * spdk_get_ticks_hz() / SPDK_SEC_TO_USEC;
141 : 1809 : }
142 : :
143 : : void
144 : 7627 : spdk_scheduler_register(struct spdk_scheduler *scheduler)
145 : : {
146 [ - + ]: 7627 : if (_scheduler_find(scheduler->name)) {
147 : 0 : SPDK_ERRLOG("scheduler named '%s' already registered.\n", scheduler->name);
148 : 0 : assert(false);
149 : : return;
150 : : }
151 : :
152 : 7627 : TAILQ_INSERT_TAIL(&g_scheduler_list, scheduler, link);
153 : : }
154 : :
155 : : uint32_t
156 : 40 : spdk_scheduler_get_scheduling_lcore(void)
157 : : {
158 : 40 : return g_scheduling_reactor->lcore;
159 : : }
160 : :
161 : : static void
162 : 4046 : reactor_construct(struct spdk_reactor *reactor, uint32_t lcore)
163 : : {
164 : 4046 : reactor->lcore = lcore;
165 : 4046 : reactor->flags.is_valid = true;
166 : :
167 : 4046 : TAILQ_INIT(&reactor->threads);
168 : 4046 : reactor->thread_count = 0;
169 : 4046 : spdk_cpuset_zero(&reactor->notify_cpuset);
170 : :
171 : 4046 : reactor->events = spdk_ring_create(SPDK_RING_TYPE_MP_SC, 65536, SPDK_ENV_SOCKET_ID_ANY);
172 [ - + ]: 4046 : if (reactor->events == NULL) {
173 : 0 : SPDK_ERRLOG("Failed to allocate events ring\n");
174 : 0 : assert(false);
175 : : }
176 : :
177 : : /* Always initialize interrupt facilities for reactor */
178 [ - + ]: 4046 : if (reactor_interrupt_init(reactor) != 0) {
179 : : /* Reactor interrupt facilities are necessary if seting app to interrupt mode. */
180 [ # # ]: 0 : if (spdk_interrupt_mode_is_enabled()) {
181 : 0 : SPDK_ERRLOG("Failed to prepare intr facilities\n");
182 : 0 : assert(false);
183 : : }
184 : 0 : return;
185 : : }
186 : :
187 : : /* If application runs with full interrupt ability,
188 : : * all reactors are going to run in interrupt mode.
189 : : */
190 [ + + ]: 4046 : if (spdk_interrupt_mode_is_enabled()) {
191 : : uint32_t i;
192 : :
193 [ + + ]: 200 : SPDK_ENV_FOREACH_CORE(i) {
194 : 151 : spdk_cpuset_set_cpu(&reactor->notify_cpuset, i, true);
195 : : }
196 : 49 : reactor->in_interrupt = true;
197 : : }
198 : : }
199 : :
200 : : struct spdk_reactor *
201 : 72910860 : spdk_reactor_get(uint32_t lcore)
202 : : {
203 : : struct spdk_reactor *reactor;
204 : :
205 [ - + ]: 72910860 : if (g_reactors == NULL) {
206 : 0 : SPDK_WARNLOG("Called spdk_reactor_get() while the g_reactors array was NULL!\n");
207 : 0 : return NULL;
208 : : }
209 : :
210 [ - + ]: 72910860 : if (lcore >= g_reactor_count) {
211 : 0 : return NULL;
212 : : }
213 : :
214 : 72910860 : reactor = &g_reactors[lcore];
215 : :
216 [ - + ]: 72910860 : if (reactor->flags.is_valid == false) {
217 : 0 : return NULL;
218 : : }
219 : :
220 : 72910860 : return reactor;
221 : : }
222 : :
223 : : static int reactor_thread_op(struct spdk_thread *thread, enum spdk_thread_op op);
224 : : static bool reactor_thread_op_supported(enum spdk_thread_op op);
225 : :
226 : : int
227 : 2854 : spdk_reactors_init(size_t msg_mempool_size)
228 : : {
229 : : struct spdk_reactor *reactor;
230 : : int rc;
231 : : uint32_t i, current_core;
232 : 1377 : char mempool_name[32];
233 : :
234 [ - + ]: 2854 : snprintf(mempool_name, sizeof(mempool_name), "evtpool_%d", getpid());
235 : 2854 : g_spdk_event_mempool = spdk_mempool_create(mempool_name,
236 : : 262144 - 1, /* Power of 2 minus 1 is optimal for memory consumption */
237 : : sizeof(struct spdk_event),
238 : : SPDK_MEMPOOL_DEFAULT_CACHE_SIZE,
239 : : SPDK_ENV_SOCKET_ID_ANY);
240 : :
241 [ - + ]: 2854 : if (g_spdk_event_mempool == NULL) {
242 : 0 : SPDK_ERRLOG("spdk_event_mempool creation failed\n");
243 : 0 : return -1;
244 : : }
245 : :
246 : : /* struct spdk_reactor must be aligned on 64 byte boundary */
247 : 2854 : g_reactor_count = spdk_env_get_last_core() + 1;
248 [ - + ]: 2854 : rc = posix_memalign((void **)&g_reactors, 64,
249 : : g_reactor_count * sizeof(struct spdk_reactor));
250 [ - + ]: 2854 : if (rc != 0) {
251 : 0 : SPDK_ERRLOG("Could not allocate array size=%u for g_reactors\n",
252 : : g_reactor_count);
253 : 0 : spdk_mempool_free(g_spdk_event_mempool);
254 : 0 : return -1;
255 : : }
256 : :
257 : 2854 : g_core_infos = calloc(g_reactor_count, sizeof(*g_core_infos));
258 [ - + ]: 2854 : if (g_core_infos == NULL) {
259 : 0 : SPDK_ERRLOG("Could not allocate memory for g_core_infos\n");
260 : 0 : spdk_mempool_free(g_spdk_event_mempool);
261 : 0 : free(g_reactors);
262 : 0 : return -ENOMEM;
263 : : }
264 : :
265 [ - + ]: 2854 : memset(g_reactors, 0, (g_reactor_count) * sizeof(struct spdk_reactor));
266 : :
267 : 2854 : rc = spdk_thread_lib_init_ext(reactor_thread_op, reactor_thread_op_supported,
268 : : sizeof(struct spdk_lw_thread), msg_mempool_size);
269 [ - + ]: 2854 : if (rc != 0) {
270 : 0 : SPDK_ERRLOG("Initialize spdk thread lib failed\n");
271 : 0 : spdk_mempool_free(g_spdk_event_mempool);
272 : 0 : free(g_reactors);
273 : 0 : free(g_core_infos);
274 : 0 : return rc;
275 : : }
276 : :
277 [ + + ]: 6896 : SPDK_ENV_FOREACH_CORE(i) {
278 : 4042 : reactor_construct(&g_reactors[i], i);
279 : : }
280 : :
281 : 2854 : current_core = spdk_env_get_current_core();
282 : 2854 : reactor = spdk_reactor_get(current_core);
283 [ - + ]: 2854 : assert(reactor != NULL);
284 : 2854 : g_scheduling_reactor = reactor;
285 : :
286 : 2854 : g_reactor_state = SPDK_REACTOR_STATE_INITIALIZED;
287 : :
288 : 2854 : return 0;
289 : : }
290 : :
291 : : void
292 : 2912 : spdk_reactors_fini(void)
293 : : {
294 : : uint32_t i;
295 : : struct spdk_reactor *reactor;
296 : :
297 [ + + ]: 2912 : if (g_reactor_state == SPDK_REACTOR_STATE_UNINITIALIZED) {
298 : 58 : return;
299 : : }
300 : :
301 : 2854 : spdk_thread_lib_fini();
302 : :
303 [ + + ]: 6896 : SPDK_ENV_FOREACH_CORE(i) {
304 : 4042 : reactor = spdk_reactor_get(i);
305 [ - + ]: 4042 : assert(reactor != NULL);
306 [ - + ]: 4042 : assert(reactor->thread_count == 0);
307 [ + - ]: 4042 : if (reactor->events != NULL) {
308 : 4042 : spdk_ring_free(reactor->events);
309 : : }
310 : :
311 : 4042 : reactor_interrupt_fini(reactor);
312 : :
313 [ + - ]: 4042 : if (g_core_infos != NULL) {
314 : 4042 : free(g_core_infos[i].thread_infos);
315 : : }
316 : : }
317 : :
318 : 2854 : spdk_mempool_free(g_spdk_event_mempool);
319 : :
320 : 2854 : free(g_reactors);
321 : 2854 : g_reactors = NULL;
322 : 2854 : free(g_core_infos);
323 : 2854 : g_core_infos = NULL;
324 : : }
325 : :
326 : : static void _reactor_set_interrupt_mode(void *arg1, void *arg2);
327 : :
328 : : static void
329 : 744 : _reactor_set_notify_cpuset(void *arg1, void *arg2)
330 : : {
331 : 744 : struct spdk_reactor *target = arg1;
332 : 744 : struct spdk_reactor *reactor = spdk_reactor_get(spdk_env_get_current_core());
333 : :
334 [ - + ]: 744 : assert(reactor != NULL);
335 [ - + ]: 744 : spdk_cpuset_set_cpu(&reactor->notify_cpuset, target->lcore, target->new_in_interrupt);
336 : 744 : }
337 : :
338 : : static void
339 : 25449 : _event_call(uint32_t lcore, spdk_event_fn fn, void *arg1, void *arg2)
340 : : {
341 : : struct spdk_event *ev;
342 : :
343 : 25449 : ev = spdk_event_allocate(lcore, fn, arg1, arg2);
344 [ - + ]: 25449 : assert(ev);
345 : 25449 : spdk_event_call(ev);
346 : 25449 : }
347 : :
348 : : static void
349 : 173 : _reactor_set_notify_cpuset_cpl(void *arg1, void *arg2)
350 : : {
351 : 173 : struct spdk_reactor *target = arg1;
352 : :
353 [ + + + + ]: 173 : if (target->new_in_interrupt == false) {
354 : 58 : target->set_interrupt_mode_in_progress = false;
355 : 58 : spdk_thread_send_msg(spdk_thread_get_app_thread(), target->set_interrupt_mode_cb_fn,
356 : : target->set_interrupt_mode_cb_arg);
357 : : } else {
358 : 115 : _event_call(target->lcore, _reactor_set_interrupt_mode, target, NULL);
359 : : }
360 : 173 : }
361 : :
362 : : static void
363 : 40 : _reactor_set_thread_interrupt_mode(void *ctx)
364 : : {
365 : 40 : struct spdk_reactor *reactor = ctx;
366 : :
367 [ - + ]: 40 : spdk_thread_set_interrupt_mode(reactor->in_interrupt);
368 : 40 : }
369 : :
370 : : static void
371 : 173 : _reactor_set_interrupt_mode(void *arg1, void *arg2)
372 : : {
373 : 173 : struct spdk_reactor *target = arg1;
374 : : struct spdk_thread *thread;
375 : : struct spdk_fd_group *grp;
376 : : struct spdk_lw_thread *lw_thread, *tmp;
377 : :
378 [ - + ]: 173 : assert(target == spdk_reactor_get(spdk_env_get_current_core()));
379 [ - + ]: 173 : assert(target != NULL);
380 [ - + - + : 173 : assert(target->in_interrupt != target->new_in_interrupt);
- + ]
381 [ - + - + : 173 : SPDK_DEBUGLOG(reactor, "Do reactor set on core %u from %s to state %s\n",
- - - - -
- - - ]
382 : : target->lcore, target->in_interrupt ? "intr" : "poll", target->new_in_interrupt ? "intr" : "poll");
383 : :
384 [ - + ]: 173 : target->in_interrupt = target->new_in_interrupt;
385 : :
386 [ + + ]: 173 : if (spdk_interrupt_mode_is_enabled()) {
387 : : /* Align spdk_thread with reactor to interrupt mode or poll mode */
388 [ + + ]: 50 : TAILQ_FOREACH_SAFE(lw_thread, &target->threads, link, tmp) {
389 : 10 : thread = spdk_thread_get_from_ctx(lw_thread);
390 [ + + + + ]: 10 : if (target->in_interrupt) {
391 : 5 : grp = spdk_thread_get_interrupt_fd_group(thread);
392 : 5 : spdk_fd_group_nest(target->fgrp, grp);
393 : : } else {
394 : 5 : grp = spdk_thread_get_interrupt_fd_group(thread);
395 : 5 : spdk_fd_group_unnest(target->fgrp, grp);
396 : : }
397 : :
398 : 10 : spdk_thread_send_msg(thread, _reactor_set_thread_interrupt_mode, target);
399 : : }
400 : : }
401 : :
402 [ + + + + ]: 173 : if (target->new_in_interrupt == false) {
403 : : /* Reactor is no longer in interrupt mode. Refresh the tsc_last to accurately
404 : : * track reactor stats. */
405 : 58 : target->tsc_last = spdk_get_ticks();
406 : 58 : spdk_for_each_reactor(_reactor_set_notify_cpuset, target, NULL, _reactor_set_notify_cpuset_cpl);
407 : : } else {
408 : 115 : uint64_t notify = 1;
409 : 115 : int rc = 0;
410 : :
411 : : /* Always trigger spdk_event and resched event in case of race condition */
412 : 115 : rc = write(target->events_fd, ¬ify, sizeof(notify));
413 [ - + ]: 115 : if (rc < 0) {
414 : 0 : SPDK_ERRLOG("failed to notify event queue: %s.\n", spdk_strerror(errno));
415 : : }
416 : 115 : rc = write(target->resched_fd, ¬ify, sizeof(notify));
417 [ - + ]: 115 : if (rc < 0) {
418 : 0 : SPDK_ERRLOG("failed to notify reschedule: %s.\n", spdk_strerror(errno));
419 : : }
420 : :
421 : 115 : target->set_interrupt_mode_in_progress = false;
422 : 115 : spdk_thread_send_msg(spdk_thread_get_app_thread(), target->set_interrupt_mode_cb_fn,
423 : : target->set_interrupt_mode_cb_arg);
424 : : }
425 : 173 : }
426 : :
427 : : int
428 : 175 : spdk_reactor_set_interrupt_mode(uint32_t lcore, bool new_in_interrupt,
429 : : spdk_reactor_set_interrupt_mode_cb cb_fn, void *cb_arg)
430 : : {
431 : : struct spdk_reactor *target;
432 : :
433 : 175 : target = spdk_reactor_get(lcore);
434 [ - + ]: 175 : if (target == NULL) {
435 : 0 : return -EINVAL;
436 : : }
437 : :
438 : : /* Eventfd has to be supported in order to use interrupt functionality. */
439 [ - + ]: 175 : if (target->fgrp == NULL) {
440 : 0 : return -ENOTSUP;
441 : : }
442 : :
443 [ - + ]: 175 : if (spdk_env_get_current_core() != g_scheduling_reactor->lcore) {
444 : 0 : SPDK_ERRLOG("It is only permitted within scheduling reactor.\n");
445 : 0 : return -EPERM;
446 : : }
447 : :
448 [ - + - + ]: 175 : if (target->in_interrupt == new_in_interrupt) {
449 : 0 : cb_fn(cb_arg);
450 : 0 : return 0;
451 : : }
452 : :
453 [ - + - + ]: 175 : if (target->set_interrupt_mode_in_progress) {
454 : 0 : SPDK_NOTICELOG("Reactor(%u) is already in progress to set interrupt mode\n", lcore);
455 : 0 : return -EBUSY;
456 : : }
457 : 175 : target->set_interrupt_mode_in_progress = true;
458 : :
459 : 175 : target->new_in_interrupt = new_in_interrupt;
460 : 175 : target->set_interrupt_mode_cb_fn = cb_fn;
461 : 175 : target->set_interrupt_mode_cb_arg = cb_arg;
462 : :
463 [ - + - + ]: 175 : SPDK_DEBUGLOG(reactor, "Starting reactor event from %d to %d\n",
464 : : spdk_env_get_current_core(), lcore);
465 : :
466 [ + + ]: 175 : if (new_in_interrupt == false) {
467 : : /* For potential race cases, when setting the reactor to poll mode,
468 : : * first change the mode of the reactor and then clear the corresponding
469 : : * bit of the notify_cpuset of each reactor.
470 : : */
471 : 58 : _event_call(lcore, _reactor_set_interrupt_mode, target, NULL);
472 : : } else {
473 : : /* For race cases, when setting the reactor to interrupt mode, first set the
474 : : * corresponding bit of the notify_cpuset of each reactor and then change the mode.
475 : : */
476 : 117 : spdk_for_each_reactor(_reactor_set_notify_cpuset, target, NULL, _reactor_set_notify_cpuset_cpl);
477 : : }
478 : :
479 : 175 : return 0;
480 : : }
481 : :
482 : : struct spdk_event *
483 : 24292285 : spdk_event_allocate(uint32_t lcore, spdk_event_fn fn, void *arg1, void *arg2)
484 : : {
485 : 24292285 : struct spdk_event *event = NULL;
486 : 24292285 : struct spdk_reactor *reactor = spdk_reactor_get(lcore);
487 : :
488 [ - + ]: 24292285 : if (!reactor) {
489 : 0 : assert(false);
490 : : return NULL;
491 : : }
492 : :
493 : 24292285 : event = spdk_mempool_get(g_spdk_event_mempool);
494 [ - + ]: 24292285 : if (event == NULL) {
495 : 0 : assert(false);
496 : : return NULL;
497 : : }
498 : :
499 : 24292285 : event->lcore = lcore;
500 : 24292285 : event->fn = fn;
501 : 24292285 : event->arg1 = arg1;
502 : 24292285 : event->arg2 = arg2;
503 : :
504 : 24292285 : return event;
505 : : }
506 : :
507 : : void
508 : 24292285 : spdk_event_call(struct spdk_event *event)
509 : : {
510 : : int rc;
511 : : struct spdk_reactor *reactor;
512 : 24292285 : struct spdk_reactor *local_reactor = NULL;
513 : 24292285 : uint32_t current_core = spdk_env_get_current_core();
514 : :
515 : 24292285 : reactor = spdk_reactor_get(event->lcore);
516 : :
517 [ - + ]: 24292285 : assert(reactor != NULL);
518 [ - + ]: 24292285 : assert(reactor->events != NULL);
519 : :
520 : 24292285 : rc = spdk_ring_enqueue(reactor->events, (void **)&event, 1, NULL);
521 [ - + ]: 24292285 : if (rc != 1) {
522 : 0 : assert(false);
523 : : }
524 : :
525 [ + - ]: 24292285 : if (current_core != SPDK_ENV_LCORE_ID_ANY) {
526 : 24292285 : local_reactor = spdk_reactor_get(current_core);
527 : : }
528 : :
529 : : /* If spdk_event_call isn't called on a reactor, always send a notification.
530 : : * If it is called on a reactor, send a notification if the destination reactor
531 : : * is indicated in interrupt mode state.
532 : : */
533 [ + - ]: 24292285 : if (spdk_unlikely(local_reactor == NULL) ||
534 [ + + ]: 24292285 : spdk_unlikely(spdk_cpuset_get_cpu(&local_reactor->notify_cpuset, event->lcore))) {
535 : 64448 : uint64_t notify = 1;
536 : :
537 : 64448 : rc = write(reactor->events_fd, ¬ify, sizeof(notify));
538 [ - + ]: 64448 : if (rc < 0) {
539 : 0 : SPDK_ERRLOG("failed to notify event queue: %s.\n", spdk_strerror(errno));
540 : : }
541 : : }
542 : 24292285 : }
543 : :
544 : : static inline int
545 : 9358035820 : event_queue_run_batch(void *arg)
546 : : {
547 : 9358035820 : struct spdk_reactor *reactor = arg;
548 : : size_t count, i;
549 : 5003002535 : void *events[SPDK_EVENT_BATCH_SIZE];
550 : :
551 : : #ifdef DEBUG
552 : : /*
553 : : * spdk_ring_dequeue() fills events and returns how many entries it wrote,
554 : : * so we will never actually read uninitialized data from events, but just to be sure
555 : : * (and to silence a static analyzer false positive), initialize the array to NULL pointers.
556 : : */
557 : 9358035820 : memset(events, 0, sizeof(events));
558 : : #endif
559 : :
560 : : /* Operate event notification if this reactor currently runs in interrupt state */
561 [ + + + + ]: 9358035820 : if (spdk_unlikely(reactor->in_interrupt)) {
562 : 64300 : uint64_t notify = 1;
563 : : int rc;
564 : :
565 : : /* There may be race between event_acknowledge and another producer's event_notify,
566 : : * so event_acknowledge should be applied ahead. And then check for self's event_notify.
567 : : * This can avoid event notification missing.
568 : : */
569 : 64300 : rc = read(reactor->events_fd, ¬ify, sizeof(notify));
570 [ - + ]: 64300 : if (rc < 0) {
571 : 0 : SPDK_ERRLOG("failed to acknowledge event queue: %s.\n", spdk_strerror(errno));
572 : 0 : return -errno;
573 : : }
574 : :
575 : 64300 : count = spdk_ring_dequeue(reactor->events, events, SPDK_EVENT_BATCH_SIZE);
576 : :
577 [ + + ]: 64300 : if (spdk_ring_count(reactor->events) != 0) {
578 : : /* Trigger new notification if there are still events in event-queue waiting for processing. */
579 : 1 : rc = write(reactor->events_fd, ¬ify, sizeof(notify));
580 [ - + ]: 1 : if (rc < 0) {
581 : 0 : SPDK_ERRLOG("failed to notify event queue: %s.\n", spdk_strerror(errno));
582 : 0 : return -errno;
583 : : }
584 : : }
585 : : } else {
586 : 9357971495 : count = spdk_ring_dequeue(reactor->events, events, SPDK_EVENT_BATCH_SIZE);
587 : : }
588 : :
589 [ + + ]: 9358035820 : if (count == 0) {
590 : 9344174532 : return 0;
591 : : }
592 : :
593 [ + + ]: 38154323 : for (i = 0; i < count; i++) {
594 : 24292265 : struct spdk_event *event = events[i];
595 : :
596 [ - + ]: 24292265 : assert(event != NULL);
597 [ - + ]: 24292265 : assert(spdk_get_thread() == NULL);
598 : 2328113 : SPDK_DTRACE_PROBE3(event_exec, event->fn,
599 : : event->arg1, event->arg2);
600 : 24292265 : event->fn(event->arg1, event->arg2);
601 : : }
602 : :
603 : 13862056 : spdk_mempool_put_bulk(g_spdk_event_mempool, events, count);
604 : :
605 : 13862056 : return (int)count;
606 : : }
607 : :
608 : : /* 1s */
609 : : #define CONTEXT_SWITCH_MONITOR_PERIOD 1000000
610 : :
611 : : static int
612 : 30869 : get_rusage(struct spdk_reactor *reactor)
613 : : {
614 : 14571 : struct rusage rusage;
615 : :
616 [ - + ]: 30869 : if (getrusage(RUSAGE_THREAD, &rusage) != 0) {
617 : 0 : return -1;
618 : : }
619 : :
620 [ + + + + ]: 30869 : if (rusage.ru_nvcsw != reactor->rusage.ru_nvcsw || rusage.ru_nivcsw != reactor->rusage.ru_nivcsw) {
621 [ - + - + ]: 29554 : SPDK_INFOLOG(reactor,
622 : : "Reactor %d: %ld voluntary context switches and %ld involuntary context switches in the last second.\n",
623 : : reactor->lcore, rusage.ru_nvcsw - reactor->rusage.ru_nvcsw,
624 : : rusage.ru_nivcsw - reactor->rusage.ru_nivcsw);
625 : : }
626 : 30869 : reactor->rusage = rusage;
627 : :
628 : 30869 : return -1;
629 : : }
630 : :
631 : : void
632 : 0 : spdk_framework_enable_context_switch_monitor(bool enable)
633 : : {
634 : : /* This global is being read by multiple threads, so this isn't
635 : : * strictly thread safe. However, we're toggling between true and
636 : : * false here, and if a thread sees the value update later than it
637 : : * should, it's no big deal. */
638 : 0 : g_framework_context_switch_monitor_enabled = enable;
639 : 0 : }
640 : :
641 : : bool
642 : 0 : spdk_framework_context_switch_monitor_enabled(void)
643 : : {
644 [ # # ]: 0 : return g_framework_context_switch_monitor_enabled;
645 : : }
646 : :
647 : : static void
648 : 3973 : _set_thread_name(const char *thread_name)
649 : : {
650 : : #if defined(__linux__)
651 : 3973 : prctl(PR_SET_NAME, thread_name, 0, 0, 0);
652 : : #elif defined(__FreeBSD__)
653 : : pthread_set_name_np(pthread_self(), thread_name);
654 : : #else
655 : : pthread_setname_np(pthread_self(), thread_name);
656 : : #endif
657 : 3973 : }
658 : :
659 : : static void
660 : 1519 : _init_thread_stats(struct spdk_reactor *reactor, struct spdk_lw_thread *lw_thread)
661 : : {
662 : 1519 : struct spdk_thread *thread = spdk_thread_get_from_ctx(lw_thread);
663 : : struct spdk_thread_stats prev_total_stats;
664 : :
665 : : /* Read total_stats before updating it to calculate stats during the last scheduling period. */
666 : 1519 : prev_total_stats = lw_thread->total_stats;
667 : :
668 : 1519 : spdk_set_thread(thread);
669 : 1519 : spdk_thread_get_stats(&lw_thread->total_stats);
670 : 1519 : spdk_set_thread(NULL);
671 : :
672 : 1519 : lw_thread->current_stats.busy_tsc = lw_thread->total_stats.busy_tsc - prev_total_stats.busy_tsc;
673 : 1519 : lw_thread->current_stats.idle_tsc = lw_thread->total_stats.idle_tsc - prev_total_stats.idle_tsc;
674 : 1519 : }
675 : :
676 : : static void
677 : 86 : _threads_reschedule_thread(struct spdk_scheduler_thread_info *thread_info)
678 : : {
679 : : struct spdk_lw_thread *lw_thread;
680 : : struct spdk_thread *thread;
681 : :
682 : 86 : thread = spdk_thread_get_by_id(thread_info->thread_id);
683 [ + + ]: 86 : if (thread == NULL) {
684 : : /* Thread no longer exists. */
685 : 10 : return;
686 : : }
687 : 76 : lw_thread = spdk_thread_get_ctx(thread);
688 [ - + ]: 76 : assert(lw_thread != NULL);
689 : :
690 : 76 : lw_thread->lcore = thread_info->lcore;
691 : 76 : lw_thread->resched = true;
692 : : }
693 : :
694 : : static void
695 : 216 : _threads_reschedule(struct spdk_scheduler_core_info *cores_info)
696 : : {
697 : : struct spdk_scheduler_core_info *core;
698 : : struct spdk_scheduler_thread_info *thread_info;
699 : : uint32_t i, j;
700 : :
701 [ + + ]: 1466 : SPDK_ENV_FOREACH_CORE(i) {
702 : 1250 : core = &cores_info[i];
703 [ + + ]: 2762 : for (j = 0; j < core->threads_count; j++) {
704 : 1512 : thread_info = &core->thread_infos[j];
705 [ + + ]: 1512 : if (thread_info->lcore != i) {
706 : 86 : _threads_reschedule_thread(thread_info);
707 : : }
708 : : }
709 : 1250 : core->threads_count = 0;
710 : 1250 : free(core->thread_infos);
711 : 1250 : core->thread_infos = NULL;
712 : : }
713 : 216 : }
714 : :
715 : : static void
716 : 216 : _reactors_scheduler_fini(void)
717 : : {
718 : : /* Reschedule based on the balancing output */
719 : 216 : _threads_reschedule(g_core_infos);
720 : :
721 : 216 : g_scheduling_in_progress = false;
722 : 216 : }
723 : :
724 : : static void
725 : 351 : _reactors_scheduler_update_core_mode(void *ctx)
726 : : {
727 : : struct spdk_reactor *reactor;
728 : : uint32_t i;
729 : 351 : int rc = 0;
730 : :
731 [ + + ]: 1474 : for (i = g_scheduler_core_number; i < SPDK_ENV_LCORE_ID_ANY; i = spdk_env_get_next_core(i)) {
732 : 1258 : reactor = spdk_reactor_get(i);
733 [ - + ]: 1258 : assert(reactor != NULL);
734 [ + + - + : 1258 : if (reactor->in_interrupt != g_core_infos[i].interrupt_mode) {
+ + ]
735 : : /* Switch next found reactor to new state */
736 [ - + ]: 135 : rc = spdk_reactor_set_interrupt_mode(i, g_core_infos[i].interrupt_mode,
737 : : _reactors_scheduler_update_core_mode, NULL);
738 [ + - ]: 135 : if (rc == 0) {
739 : : /* Set core to start with after callback completes */
740 : 135 : g_scheduler_core_number = spdk_env_get_next_core(i);
741 : 135 : return;
742 : : }
743 : : }
744 : : }
745 : 216 : _reactors_scheduler_fini();
746 : : }
747 : :
748 : : static void
749 : 3 : _reactors_scheduler_cancel(void *arg1, void *arg2)
750 : : {
751 : : struct spdk_scheduler_core_info *core;
752 : : uint32_t i;
753 : :
754 [ + + ]: 15 : SPDK_ENV_FOREACH_CORE(i) {
755 : 12 : core = &g_core_infos[i];
756 : 12 : core->threads_count = 0;
757 : 12 : free(core->thread_infos);
758 : 12 : core->thread_infos = NULL;
759 : : }
760 : :
761 : 3 : g_scheduling_in_progress = false;
762 : 3 : }
763 : :
764 : : static void
765 : 221 : _reactors_scheduler_balance(void *arg1, void *arg2)
766 : : {
767 : 221 : struct spdk_scheduler *scheduler = spdk_scheduler_get();
768 : :
769 [ + - + + ]: 221 : if (g_reactor_state != SPDK_REACTOR_STATE_RUNNING || scheduler == NULL) {
770 : 3 : _reactors_scheduler_cancel(NULL, NULL);
771 : 3 : return;
772 : : }
773 : :
774 : 218 : scheduler->balance(g_core_infos, g_reactor_count);
775 : :
776 : 218 : g_scheduler_core_number = spdk_env_get_first_core();
777 : 218 : _reactors_scheduler_update_core_mode(NULL);
778 : : }
779 : :
780 : : /* Phase 1 of thread scheduling is to gather metrics on the existing threads */
781 : : static void
782 : 1270 : _reactors_scheduler_gather_metrics(void *arg1, void *arg2)
783 : : {
784 : : struct spdk_scheduler_core_info *core_info;
785 : : struct spdk_lw_thread *lw_thread;
786 : : struct spdk_thread *thread;
787 : : struct spdk_reactor *reactor;
788 : : uint32_t next_core;
789 : 1270 : uint32_t i = 0;
790 : :
791 : 1270 : reactor = spdk_reactor_get(spdk_env_get_current_core());
792 [ - + ]: 1270 : assert(reactor != NULL);
793 : 1270 : core_info = &g_core_infos[reactor->lcore];
794 : 1270 : core_info->lcore = reactor->lcore;
795 : 1270 : core_info->current_idle_tsc = reactor->idle_tsc - core_info->total_idle_tsc;
796 : 1270 : core_info->total_idle_tsc = reactor->idle_tsc;
797 : 1270 : core_info->current_busy_tsc = reactor->busy_tsc - core_info->total_busy_tsc;
798 : 1270 : core_info->total_busy_tsc = reactor->busy_tsc;
799 [ - + ]: 1270 : core_info->interrupt_mode = reactor->in_interrupt;
800 : 1270 : core_info->threads_count = 0;
801 : :
802 [ - + - + ]: 1270 : SPDK_DEBUGLOG(reactor, "Gathering metrics on %u\n", reactor->lcore);
803 : :
804 [ + + ]: 1270 : if (reactor->thread_count > 0) {
805 : 351 : core_info->thread_infos = calloc(reactor->thread_count, sizeof(*core_info->thread_infos));
806 [ - + ]: 351 : if (core_info->thread_infos == NULL) {
807 : 0 : SPDK_ERRLOG("Failed to allocate memory when gathering metrics on %u\n", reactor->lcore);
808 : :
809 : : /* Cancel this round of schedule work */
810 : 0 : _event_call(g_scheduling_reactor->lcore, _reactors_scheduler_cancel, NULL, NULL);
811 : 0 : return;
812 : : }
813 : :
814 [ + + ]: 1870 : TAILQ_FOREACH(lw_thread, &reactor->threads, link) {
815 : 1519 : _init_thread_stats(reactor, lw_thread);
816 : :
817 : 1519 : core_info->thread_infos[i].lcore = lw_thread->lcore;
818 : 1519 : thread = spdk_thread_get_from_ctx(lw_thread);
819 [ - + ]: 1519 : assert(thread != NULL);
820 : 1519 : core_info->thread_infos[i].thread_id = spdk_thread_get_id(thread);
821 : 1519 : core_info->thread_infos[i].total_stats = lw_thread->total_stats;
822 : 1519 : core_info->thread_infos[i].current_stats = lw_thread->current_stats;
823 : 1519 : core_info->threads_count++;
824 [ - + ]: 1519 : assert(core_info->threads_count <= reactor->thread_count);
825 : 1519 : i++;
826 : : }
827 : : }
828 : :
829 : 1270 : next_core = spdk_env_get_next_core(reactor->lcore);
830 [ + + ]: 1270 : if (next_core == UINT32_MAX) {
831 : 221 : next_core = spdk_env_get_first_core();
832 : : }
833 : :
834 : : /* If we've looped back around to the scheduler thread, move to the next phase */
835 [ + + ]: 1270 : if (next_core == g_scheduling_reactor->lcore) {
836 : : /* Phase 2 of scheduling is rebalancing - deciding which threads to move where */
837 : 221 : _event_call(next_core, _reactors_scheduler_balance, NULL, NULL);
838 : 221 : return;
839 : : }
840 : :
841 : 1049 : _event_call(next_core, _reactors_scheduler_gather_metrics, NULL, NULL);
842 : : }
843 : :
844 : : static int _reactor_schedule_thread(struct spdk_thread *thread);
845 : : static uint64_t g_rusage_period;
846 : :
847 : : static void
848 : 7385 : _reactor_remove_lw_thread(struct spdk_reactor *reactor, struct spdk_lw_thread *lw_thread)
849 : : {
850 : 7385 : struct spdk_thread *thread = spdk_thread_get_from_ctx(lw_thread);
851 : : struct spdk_fd_group *grp;
852 : :
853 [ + + ]: 7385 : TAILQ_REMOVE(&reactor->threads, lw_thread, link);
854 [ - + ]: 7385 : assert(reactor->thread_count > 0);
855 : 7385 : reactor->thread_count--;
856 : :
857 : : /* Operate thread intr if running with full interrupt ability */
858 [ + + ]: 7385 : if (spdk_interrupt_mode_is_enabled()) {
859 [ + + + - ]: 30 : if (reactor->in_interrupt) {
860 : 30 : grp = spdk_thread_get_interrupt_fd_group(thread);
861 : 30 : spdk_fd_group_unnest(reactor->fgrp, grp);
862 : : }
863 : : }
864 : 7385 : }
865 : :
866 : : static bool
867 :10762601852 : reactor_post_process_lw_thread(struct spdk_reactor *reactor, struct spdk_lw_thread *lw_thread)
868 : : {
869 :10762601852 : struct spdk_thread *thread = spdk_thread_get_from_ctx(lw_thread);
870 : :
871 [ + + + + ]:10762601852 : if (spdk_unlikely(spdk_thread_is_exited(thread) &&
872 : : spdk_thread_is_idle(thread))) {
873 : 3622 : _reactor_remove_lw_thread(reactor, lw_thread);
874 : 3622 : spdk_thread_destroy(thread);
875 : 3622 : return true;
876 : : }
877 : :
878 [ + + + + :10762588536 : if (spdk_unlikely(lw_thread->resched && !spdk_thread_is_bound(thread))) {
+ + ]
879 : 81 : lw_thread->resched = false;
880 : 81 : _reactor_remove_lw_thread(reactor, lw_thread);
881 : 81 : _reactor_schedule_thread(thread);
882 : 81 : return true;
883 : : }
884 : :
885 :10762588459 : return false;
886 : : }
887 : :
888 : : static void
889 : 4384224 : reactor_interrupt_run(struct spdk_reactor *reactor)
890 : : {
891 : 4384224 : int block_timeout = -1; /* _EPOLL_WAIT_FOREVER */
892 : :
893 : 4384224 : spdk_fd_group_wait(reactor->fgrp, block_timeout);
894 : 4384224 : }
895 : :
896 : : static void
897 : 9357971207 : _reactor_run(struct spdk_reactor *reactor)
898 : : {
899 : : struct spdk_thread *thread;
900 : : struct spdk_lw_thread *lw_thread, *tmp;
901 : : uint64_t now;
902 : : int rc;
903 : :
904 : 9357971207 : event_queue_run_batch(reactor);
905 : :
906 : : /* If no threads are present on the reactor,
907 : : * tsc_last gets outdated. Update it to track
908 : : * thread execution time correctly. */
909 [ + + ]: 9357971207 : if (spdk_unlikely(TAILQ_EMPTY(&reactor->threads))) {
910 : 1902134208 : now = spdk_get_ticks();
911 : 1902134208 : reactor->idle_tsc += now - reactor->tsc_last;
912 : 1902134208 : reactor->tsc_last = now;
913 : 1902134208 : return;
914 : : }
915 : :
916 [ + + ]:18218433834 : TAILQ_FOREACH_SAFE(lw_thread, &reactor->threads, link, tmp) {
917 :10762601835 : thread = spdk_thread_get_from_ctx(lw_thread);
918 :10762601835 : rc = spdk_thread_poll(thread, 0, reactor->tsc_last);
919 : :
920 :10762601835 : now = spdk_thread_get_last_tsc(thread);
921 [ + + ]:10762601835 : if (rc == 0) {
922 :10683017682 : reactor->idle_tsc += now - reactor->tsc_last;
923 [ + - ]: 79578253 : } else if (rc > 0) {
924 : 79578253 : reactor->busy_tsc += now - reactor->tsc_last;
925 : : }
926 :10762601835 : reactor->tsc_last = now;
927 : :
928 :10762601835 : reactor_post_process_lw_thread(reactor, lw_thread);
929 : : }
930 : : }
931 : :
932 : : static int
933 : 3973 : reactor_run(void *arg)
934 : : {
935 : 3973 : struct spdk_reactor *reactor = arg;
936 : : struct spdk_thread *thread;
937 : : struct spdk_lw_thread *lw_thread, *tmp;
938 : 1738 : char thread_name[32];
939 : 3973 : uint64_t last_sched = 0;
940 : :
941 : 3973 : SPDK_NOTICELOG("Reactor started on core %u\n", reactor->lcore);
942 : :
943 : : /* Rename the POSIX thread because the reactor is tied to the POSIX
944 : : * thread in the SPDK event library.
945 : : */
946 : 3973 : snprintf(thread_name, sizeof(thread_name), "reactor_%u", reactor->lcore);
947 : 3973 : _set_thread_name(thread_name);
948 : :
949 : 3973 : reactor->tsc_last = spdk_get_ticks();
950 : :
951 : : while (1) {
952 : : /* Execute interrupt process fn if this reactor currently runs in interrupt state */
953 [ + + + + ]: 9362355287 : if (spdk_unlikely(reactor->in_interrupt)) {
954 : 4384208 : reactor_interrupt_run(reactor);
955 : : } else {
956 : 9357971071 : _reactor_run(reactor);
957 : : }
958 : :
959 [ + + + - ]: 9362355287 : if (g_framework_context_switch_monitor_enabled) {
960 [ + + ]: 9362355287 : if ((reactor->last_rusage + g_rusage_period) < reactor->tsc_last) {
961 : 30869 : get_rusage(reactor);
962 : 30869 : reactor->last_rusage = reactor->tsc_last;
963 : : }
964 : : }
965 : :
966 [ + + + + : 9362355287 : if (spdk_unlikely(g_scheduler_period > 0 &&
+ + + + +
+ + + +
+ ]
967 : : (reactor->tsc_last - last_sched) > g_scheduler_period &&
968 : : reactor == g_scheduling_reactor &&
969 : : !g_scheduling_in_progress)) {
970 : 197 : last_sched = reactor->tsc_last;
971 : 197 : g_scheduling_in_progress = true;
972 : 197 : _reactors_scheduler_gather_metrics(NULL, NULL);
973 : : }
974 : :
975 [ + + ]: 9362355287 : if (g_reactor_state != SPDK_REACTOR_STATE_RUNNING) {
976 : 3973 : break;
977 : : }
978 : : }
979 : :
980 [ + + ]: 7655 : TAILQ_FOREACH(lw_thread, &reactor->threads, link) {
981 : 3682 : thread = spdk_thread_get_from_ctx(lw_thread);
982 : : /* All threads should have already had spdk_thread_exit() called on them, except
983 : : * for the app thread.
984 : : */
985 [ + + ]: 3682 : if (spdk_thread_is_running(thread)) {
986 [ - + ]: 2817 : if (!spdk_thread_is_app_thread(thread)) {
987 : 0 : SPDK_ERRLOG("spdk_thread_exit() was not called on thread '%s'\n",
988 : : spdk_thread_get_name(thread));
989 : 0 : SPDK_ERRLOG("This will result in a non-zero exit code in a future release.\n");
990 : : }
991 : 2817 : spdk_set_thread(thread);
992 : 2817 : spdk_thread_exit(thread);
993 : : }
994 : : }
995 : :
996 [ + + ]: 9906 : while (!TAILQ_EMPTY(&reactor->threads)) {
997 [ + + ]: 12432 : TAILQ_FOREACH_SAFE(lw_thread, &reactor->threads, link, tmp) {
998 : 6499 : thread = spdk_thread_get_from_ctx(lw_thread);
999 : 6499 : spdk_set_thread(thread);
1000 [ + + ]: 6499 : if (spdk_thread_is_exited(thread)) {
1001 : 3682 : _reactor_remove_lw_thread(reactor, lw_thread);
1002 : 3682 : spdk_thread_destroy(thread);
1003 : : } else {
1004 [ + + + + ]: 2817 : if (spdk_unlikely(reactor->in_interrupt)) {
1005 : 16 : reactor_interrupt_run(reactor);
1006 : : } else {
1007 : 2801 : spdk_thread_poll(thread, 0, 0);
1008 : : }
1009 : : }
1010 : : }
1011 : : }
1012 : :
1013 : 3973 : return 0;
1014 : : }
1015 : :
1016 : : int
1017 : 10 : spdk_app_parse_core_mask(const char *mask, struct spdk_cpuset *cpumask)
1018 : : {
1019 : : int ret;
1020 : : const struct spdk_cpuset *validmask;
1021 : :
1022 : 10 : ret = spdk_cpuset_parse(cpumask, mask);
1023 [ - + ]: 10 : if (ret < 0) {
1024 : 0 : return ret;
1025 : : }
1026 : :
1027 : 10 : validmask = spdk_app_get_core_mask();
1028 : 10 : spdk_cpuset_and(cpumask, validmask);
1029 : :
1030 : 10 : return 0;
1031 : : }
1032 : :
1033 : : const struct spdk_cpuset *
1034 : 239 : spdk_app_get_core_mask(void)
1035 : : {
1036 : 239 : return &g_reactor_core_mask;
1037 : : }
1038 : :
1039 : : void
1040 : 2817 : spdk_reactors_start(void)
1041 : : {
1042 : : struct spdk_reactor *reactor;
1043 : : uint32_t i, current_core;
1044 : : int rc;
1045 : :
1046 : 2817 : g_rusage_period = (CONTEXT_SWITCH_MONITOR_PERIOD * spdk_get_ticks_hz()) / SPDK_SEC_TO_USEC;
1047 : 2817 : g_reactor_state = SPDK_REACTOR_STATE_RUNNING;
1048 : : /* Reinitialize to false, in case the app framework is restarting in the same process. */
1049 : 2817 : g_stopping_reactors = false;
1050 : :
1051 : 2817 : current_core = spdk_env_get_current_core();
1052 [ + + ]: 6754 : SPDK_ENV_FOREACH_CORE(i) {
1053 [ + + ]: 3937 : if (i != current_core) {
1054 : 1120 : reactor = spdk_reactor_get(i);
1055 [ - + ]: 1120 : if (reactor == NULL) {
1056 : 0 : continue;
1057 : : }
1058 : :
1059 : 1120 : rc = spdk_env_thread_launch_pinned(reactor->lcore, reactor_run, reactor);
1060 [ - + ]: 1120 : if (rc < 0) {
1061 : 0 : SPDK_ERRLOG("Unable to start reactor thread on core %u\n", reactor->lcore);
1062 : 0 : assert(false);
1063 : : return;
1064 : : }
1065 : : }
1066 : 3937 : spdk_cpuset_set_cpu(&g_reactor_core_mask, i, true);
1067 : : }
1068 : :
1069 : : /* Start the main reactor */
1070 : 2817 : reactor = spdk_reactor_get(current_core);
1071 [ - + ]: 2817 : assert(reactor != NULL);
1072 : 2817 : reactor_run(reactor);
1073 : :
1074 : 2817 : spdk_env_thread_wait_all();
1075 : :
1076 : 2817 : g_reactor_state = SPDK_REACTOR_STATE_SHUTDOWN;
1077 : : }
1078 : :
1079 : : static void
1080 : 2817 : _reactors_stop(void *arg1, void *arg2)
1081 : : {
1082 : : uint32_t i;
1083 : : int rc;
1084 : : struct spdk_reactor *reactor;
1085 : : struct spdk_reactor *local_reactor;
1086 : 2817 : uint64_t notify = 1;
1087 : :
1088 : 2817 : g_reactor_state = SPDK_REACTOR_STATE_EXITING;
1089 : 2817 : local_reactor = spdk_reactor_get(spdk_env_get_current_core());
1090 : :
1091 [ + + ]: 6754 : SPDK_ENV_FOREACH_CORE(i) {
1092 : : /* If spdk_event_call isn't called on a reactor, always send a notification.
1093 : : * If it is called on a reactor, send a notification if the destination reactor
1094 : : * is indicated in interrupt mode state.
1095 : : */
1096 [ + - + + ]: 3937 : if (local_reactor == NULL || spdk_cpuset_get_cpu(&local_reactor->notify_cpuset, i)) {
1097 : 106 : reactor = spdk_reactor_get(i);
1098 [ - + ]: 106 : assert(reactor != NULL);
1099 : 106 : rc = write(reactor->events_fd, ¬ify, sizeof(notify));
1100 [ - + ]: 106 : if (rc < 0) {
1101 : 0 : SPDK_ERRLOG("failed to notify event queue for reactor(%u): %s.\n", i, spdk_strerror(errno));
1102 : 0 : continue;
1103 : : }
1104 : : }
1105 : : }
1106 : 2817 : }
1107 : :
1108 : : static void
1109 : 3937 : nop(void *arg1, void *arg2)
1110 : : {
1111 : 3937 : }
1112 : :
1113 : : void
1114 : 2817 : spdk_reactors_stop(void *arg1)
1115 : : {
1116 : 2817 : spdk_for_each_reactor(nop, NULL, NULL, _reactors_stop);
1117 : 2817 : }
1118 : :
1119 : : static pthread_mutex_t g_scheduler_mtx = PTHREAD_MUTEX_INITIALIZER;
1120 : : static uint32_t g_next_core = UINT32_MAX;
1121 : :
1122 : : static void
1123 : 7393 : _schedule_thread(void *arg1, void *arg2)
1124 : : {
1125 : 7393 : struct spdk_lw_thread *lw_thread = arg1;
1126 : : struct spdk_thread *thread;
1127 : : struct spdk_reactor *reactor;
1128 : : uint32_t current_core;
1129 : : struct spdk_fd_group *grp;
1130 : :
1131 : 7393 : current_core = spdk_env_get_current_core();
1132 : 7393 : reactor = spdk_reactor_get(current_core);
1133 [ - + ]: 7393 : assert(reactor != NULL);
1134 : :
1135 : : /* Update total_stats to reflect state of thread
1136 : : * at the end of the move. */
1137 : 7393 : thread = spdk_thread_get_from_ctx(lw_thread);
1138 : 7393 : spdk_set_thread(thread);
1139 : 7393 : spdk_thread_get_stats(&lw_thread->total_stats);
1140 : 7393 : spdk_set_thread(NULL);
1141 : :
1142 : 7393 : lw_thread->lcore = current_core;
1143 : :
1144 : 7393 : TAILQ_INSERT_TAIL(&reactor->threads, lw_thread, link);
1145 : 7393 : reactor->thread_count++;
1146 : :
1147 : : /* Operate thread intr if running with full interrupt ability */
1148 [ + + ]: 7393 : if (spdk_interrupt_mode_is_enabled()) {
1149 : : int rc;
1150 : :
1151 [ + + + - ]: 30 : if (reactor->in_interrupt) {
1152 : 30 : grp = spdk_thread_get_interrupt_fd_group(thread);
1153 : 30 : rc = spdk_fd_group_nest(reactor->fgrp, grp);
1154 [ - + ]: 30 : if (rc < 0) {
1155 : 0 : SPDK_ERRLOG("Failed to schedule spdk_thread: %s.\n", spdk_strerror(-rc));
1156 : : }
1157 : : }
1158 : :
1159 : : /* Align spdk_thread with reactor to interrupt mode or poll mode */
1160 : 30 : spdk_thread_send_msg(thread, _reactor_set_thread_interrupt_mode, reactor);
1161 : : }
1162 : 7393 : }
1163 : :
1164 : : static int
1165 : 7394 : _reactor_schedule_thread(struct spdk_thread *thread)
1166 : : {
1167 : : uint32_t core;
1168 : : struct spdk_lw_thread *lw_thread;
1169 : 7394 : struct spdk_event *evt = NULL;
1170 : : struct spdk_cpuset *cpumask;
1171 : : uint32_t i;
1172 : 7394 : struct spdk_reactor *local_reactor = NULL;
1173 : 7394 : uint32_t current_lcore = spdk_env_get_current_core();
1174 : 3449 : struct spdk_cpuset polling_cpumask;
1175 : 3449 : struct spdk_cpuset valid_cpumask;
1176 : :
1177 : 7394 : cpumask = spdk_thread_get_cpumask(thread);
1178 : :
1179 : 7394 : lw_thread = spdk_thread_get_ctx(thread);
1180 [ - + ]: 7394 : assert(lw_thread != NULL);
1181 : 7394 : core = lw_thread->lcore;
1182 [ - + ]: 7394 : memset(lw_thread, 0, sizeof(*lw_thread));
1183 : :
1184 [ + - ]: 7394 : if (current_lcore != SPDK_ENV_LCORE_ID_ANY) {
1185 : 7394 : local_reactor = spdk_reactor_get(current_lcore);
1186 [ - + ]: 7394 : assert(local_reactor);
1187 : : }
1188 : :
1189 : : /* When interrupt ability of spdk_thread is not enabled and the current
1190 : : * reactor runs on DPDK thread, skip reactors which are in interrupt mode.
1191 : : */
1192 [ + + + - ]: 7394 : if (!spdk_interrupt_mode_is_enabled() && local_reactor != NULL) {
1193 : : /* Get the cpumask of all reactors in polling */
1194 : 7364 : spdk_cpuset_zero(&polling_cpumask);
1195 [ + + ]: 20759 : SPDK_ENV_FOREACH_CORE(i) {
1196 : 13395 : spdk_cpuset_set_cpu(&polling_cpumask, i, true);
1197 : : }
1198 : 7364 : spdk_cpuset_xor(&polling_cpumask, &local_reactor->notify_cpuset);
1199 : :
1200 [ + + ]: 7364 : if (core == SPDK_ENV_LCORE_ID_ANY) {
1201 : : /* Get the cpumask of all valid reactors which are suggested and also in polling */
1202 : 7297 : spdk_cpuset_copy(&valid_cpumask, &polling_cpumask);
1203 : 7297 : spdk_cpuset_and(&valid_cpumask, spdk_thread_get_cpumask(thread));
1204 : :
1205 : : /* If there are any valid reactors, spdk_thread should be scheduled
1206 : : * into one of the valid reactors.
1207 : : * If there is no valid reactors, spdk_thread should be scheduled
1208 : : * into one of the polling reactors.
1209 : : */
1210 [ + + ]: 7297 : if (spdk_cpuset_count(&valid_cpumask) != 0) {
1211 : 7160 : cpumask = &valid_cpumask;
1212 : : } else {
1213 : 137 : cpumask = &polling_cpumask;
1214 : : }
1215 [ - + ]: 67 : } else if (!spdk_cpuset_get_cpu(&polling_cpumask, core)) {
1216 : : /* If specified reactor is not in polling, spdk_thread should be scheduled
1217 : : * into one of the polling reactors.
1218 : : */
1219 : 0 : core = SPDK_ENV_LCORE_ID_ANY;
1220 : 0 : cpumask = &polling_cpumask;
1221 : : }
1222 : : }
1223 : :
1224 [ - + ]: 7394 : pthread_mutex_lock(&g_scheduler_mtx);
1225 [ + + ]: 7394 : if (core == SPDK_ENV_LCORE_ID_ANY) {
1226 [ + - ]: 8755 : for (i = 0; i < spdk_env_get_core_count(); i++) {
1227 [ + + ]: 8755 : if (g_next_core >= g_reactor_count) {
1228 : 6028 : g_next_core = spdk_env_get_first_core();
1229 : : }
1230 : 8755 : core = g_next_core;
1231 : 8755 : g_next_core = spdk_env_get_next_core(g_next_core);
1232 : :
1233 [ + + ]: 8755 : if (spdk_cpuset_get_cpu(cpumask, core)) {
1234 : 7327 : break;
1235 : : }
1236 : : }
1237 : : }
1238 : :
1239 : 7394 : evt = spdk_event_allocate(core, _schedule_thread, lw_thread, NULL);
1240 : :
1241 [ - + ]: 7394 : pthread_mutex_unlock(&g_scheduler_mtx);
1242 : :
1243 [ - + ]: 7394 : assert(evt != NULL);
1244 [ - + ]: 7394 : if (evt == NULL) {
1245 : 0 : SPDK_ERRLOG("Unable to schedule thread on requested core mask.\n");
1246 : 0 : return -1;
1247 : : }
1248 : :
1249 : 7394 : lw_thread->tsc_start = spdk_get_ticks();
1250 : :
1251 : 7394 : spdk_event_call(evt);
1252 : :
1253 : 7394 : return 0;
1254 : : }
1255 : :
1256 : : static void
1257 : 38 : _reactor_request_thread_reschedule(struct spdk_thread *thread)
1258 : : {
1259 : : struct spdk_lw_thread *lw_thread;
1260 : : struct spdk_reactor *reactor;
1261 : : uint32_t current_core;
1262 : :
1263 [ - + ]: 38 : assert(thread == spdk_get_thread());
1264 : :
1265 : 38 : lw_thread = spdk_thread_get_ctx(thread);
1266 : :
1267 [ - + ]: 38 : assert(lw_thread != NULL);
1268 : 38 : lw_thread->resched = true;
1269 : 38 : lw_thread->lcore = SPDK_ENV_LCORE_ID_ANY;
1270 : :
1271 : 38 : current_core = spdk_env_get_current_core();
1272 : 38 : reactor = spdk_reactor_get(current_core);
1273 [ - + ]: 38 : assert(reactor != NULL);
1274 : :
1275 : : /* Send a notification if the destination reactor is indicated in intr mode state */
1276 [ + + ]: 38 : if (spdk_unlikely(spdk_cpuset_get_cpu(&reactor->notify_cpuset, reactor->lcore))) {
1277 : 30 : uint64_t notify = 1;
1278 : :
1279 [ - + ]: 30 : if (write(reactor->resched_fd, ¬ify, sizeof(notify)) < 0) {
1280 : 0 : SPDK_ERRLOG("failed to notify reschedule: %s.\n", spdk_strerror(errno));
1281 : : }
1282 : : }
1283 : 38 : }
1284 : :
1285 : : static int
1286 : 7351 : reactor_thread_op(struct spdk_thread *thread, enum spdk_thread_op op)
1287 : : {
1288 : : struct spdk_lw_thread *lw_thread;
1289 : :
1290 [ + + - ]: 7351 : switch (op) {
1291 : 7313 : case SPDK_THREAD_OP_NEW:
1292 : 7313 : lw_thread = spdk_thread_get_ctx(thread);
1293 : 7313 : lw_thread->lcore = SPDK_ENV_LCORE_ID_ANY;
1294 : 7313 : return _reactor_schedule_thread(thread);
1295 : 38 : case SPDK_THREAD_OP_RESCHED:
1296 : 38 : _reactor_request_thread_reschedule(thread);
1297 : 38 : return 0;
1298 : 0 : default:
1299 : 0 : return -ENOTSUP;
1300 : : }
1301 : : }
1302 : :
1303 : : static bool
1304 : 7331 : reactor_thread_op_supported(enum spdk_thread_op op)
1305 : : {
1306 [ + - ]: 7331 : switch (op) {
1307 : 7331 : case SPDK_THREAD_OP_NEW:
1308 : : case SPDK_THREAD_OP_RESCHED:
1309 : 7331 : return true;
1310 : 0 : default:
1311 : 0 : return false;
1312 : : }
1313 : : }
1314 : :
1315 : : struct call_reactor {
1316 : : uint32_t cur_core;
1317 : : spdk_event_fn fn;
1318 : : void *arg1;
1319 : : void *arg2;
1320 : :
1321 : : uint32_t orig_core;
1322 : : spdk_event_fn cpl;
1323 : : };
1324 : :
1325 : : static void
1326 : 88797 : on_reactor(void *arg1, void *arg2)
1327 : : {
1328 : 88797 : struct call_reactor *cr = arg1;
1329 : : struct spdk_event *evt;
1330 : :
1331 : 88797 : cr->fn(cr->arg1, cr->arg2);
1332 : :
1333 : 88797 : cr->cur_core = spdk_env_get_next_core(cr->cur_core);
1334 : :
1335 [ + + ]: 88797 : if (cr->cur_core >= g_reactor_count) {
1336 [ - + - + ]: 24006 : SPDK_DEBUGLOG(reactor, "Completed reactor iteration\n");
1337 : :
1338 : 24006 : evt = spdk_event_allocate(cr->orig_core, cr->cpl, cr->arg1, cr->arg2);
1339 : 24006 : free(cr);
1340 : : } else {
1341 [ - + - + ]: 64791 : SPDK_DEBUGLOG(reactor, "Continuing reactor iteration to %d\n",
1342 : : cr->cur_core);
1343 : :
1344 : 64791 : evt = spdk_event_allocate(cr->cur_core, on_reactor, arg1, NULL);
1345 : : }
1346 [ - + ]: 88797 : assert(evt != NULL);
1347 : 88797 : spdk_event_call(evt);
1348 : 88797 : }
1349 : :
1350 : : void
1351 : 24028 : spdk_for_each_reactor(spdk_event_fn fn, void *arg1, void *arg2, spdk_event_fn cpl)
1352 : : {
1353 : : struct call_reactor *cr;
1354 : :
1355 : : /* When the application framework is shutting down, we will send one
1356 : : * final for_each_reactor operation with completion callback _reactors_stop,
1357 : : * to flush any existing for_each_reactor operations to avoid any memory
1358 : : * leaks. We use a mutex here to protect a boolean flag that will ensure
1359 : : * we don't start any more operations once we've started shutting down.
1360 : : */
1361 [ - + ]: 24028 : pthread_mutex_lock(&g_stopping_reactors_mtx);
1362 [ + + + + ]: 24028 : if (g_stopping_reactors) {
1363 [ - + ]: 22 : pthread_mutex_unlock(&g_stopping_reactors_mtx);
1364 : 22 : return;
1365 [ + + ]: 24006 : } else if (cpl == _reactors_stop) {
1366 : 2817 : g_stopping_reactors = true;
1367 : : }
1368 [ - + ]: 24006 : pthread_mutex_unlock(&g_stopping_reactors_mtx);
1369 : :
1370 : 24006 : cr = calloc(1, sizeof(*cr));
1371 [ - + ]: 24006 : if (!cr) {
1372 : 0 : SPDK_ERRLOG("Unable to perform reactor iteration\n");
1373 : 0 : cpl(arg1, arg2);
1374 : 0 : return;
1375 : : }
1376 : :
1377 : 24006 : cr->fn = fn;
1378 : 24006 : cr->arg1 = arg1;
1379 : 24006 : cr->arg2 = arg2;
1380 : 24006 : cr->cpl = cpl;
1381 : 24006 : cr->orig_core = spdk_env_get_current_core();
1382 : 24006 : cr->cur_core = spdk_env_get_first_core();
1383 : :
1384 [ - + - + ]: 24006 : SPDK_DEBUGLOG(reactor, "Starting reactor iteration from %d\n", cr->orig_core);
1385 : :
1386 : 24006 : _event_call(cr->cur_core, on_reactor, cr, NULL);
1387 : : }
1388 : :
1389 : : #ifdef __linux__
1390 : : static int
1391 : 123 : reactor_schedule_thread_event(void *arg)
1392 : : {
1393 : 123 : struct spdk_reactor *reactor = arg;
1394 : : struct spdk_lw_thread *lw_thread, *tmp;
1395 : 123 : uint32_t count = 0;
1396 : 123 : uint64_t notify = 1;
1397 : :
1398 [ - + - + ]: 123 : assert(reactor->in_interrupt);
1399 : :
1400 [ - + ]: 123 : if (read(reactor->resched_fd, ¬ify, sizeof(notify)) < 0) {
1401 : 0 : SPDK_ERRLOG("failed to acknowledge reschedule: %s.\n", spdk_strerror(errno));
1402 : 0 : return -errno;
1403 : : }
1404 : :
1405 [ + + ]: 143 : TAILQ_FOREACH_SAFE(lw_thread, &reactor->threads, link, tmp) {
1406 : 20 : count += reactor_post_process_lw_thread(reactor, lw_thread) ? 1 : 0;
1407 : : }
1408 : :
1409 : 123 : return count;
1410 : : }
1411 : :
1412 : : static int
1413 : 4046 : reactor_interrupt_init(struct spdk_reactor *reactor)
1414 : : {
1415 : : int rc;
1416 : :
1417 : 4046 : rc = spdk_fd_group_create(&reactor->fgrp);
1418 [ - + ]: 4046 : if (rc != 0) {
1419 : 0 : return rc;
1420 : : }
1421 : :
1422 : 4046 : reactor->resched_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
1423 [ - + ]: 4046 : if (reactor->resched_fd < 0) {
1424 : 0 : rc = -EBADF;
1425 : 0 : goto err;
1426 : : }
1427 : :
1428 : 4046 : rc = SPDK_FD_GROUP_ADD(reactor->fgrp, reactor->resched_fd, reactor_schedule_thread_event,
1429 : : reactor);
1430 [ - + ]: 4046 : if (rc) {
1431 : 0 : close(reactor->resched_fd);
1432 : 0 : goto err;
1433 : : }
1434 : :
1435 : 4046 : reactor->events_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
1436 [ - + ]: 4046 : if (reactor->events_fd < 0) {
1437 : 0 : spdk_fd_group_remove(reactor->fgrp, reactor->resched_fd);
1438 : 0 : close(reactor->resched_fd);
1439 : :
1440 : 0 : rc = -EBADF;
1441 : 0 : goto err;
1442 : : }
1443 : :
1444 : 4046 : rc = SPDK_FD_GROUP_ADD(reactor->fgrp, reactor->events_fd,
1445 : : event_queue_run_batch, reactor);
1446 [ - + ]: 4046 : if (rc) {
1447 : 0 : spdk_fd_group_remove(reactor->fgrp, reactor->resched_fd);
1448 : 0 : close(reactor->resched_fd);
1449 : 0 : close(reactor->events_fd);
1450 : 0 : goto err;
1451 : : }
1452 : :
1453 : 4046 : return 0;
1454 : :
1455 : 0 : err:
1456 : 0 : spdk_fd_group_destroy(reactor->fgrp);
1457 : 0 : reactor->fgrp = NULL;
1458 : 0 : return rc;
1459 : : }
1460 : : #else
1461 : : static int
1462 : : reactor_interrupt_init(struct spdk_reactor *reactor)
1463 : : {
1464 : : return -ENOTSUP;
1465 : : }
1466 : : #endif
1467 : :
1468 : : static void
1469 : 4046 : reactor_interrupt_fini(struct spdk_reactor *reactor)
1470 : : {
1471 : 4046 : struct spdk_fd_group *fgrp = reactor->fgrp;
1472 : :
1473 [ - + ]: 4046 : if (!fgrp) {
1474 : 0 : return;
1475 : : }
1476 : :
1477 : 4046 : spdk_fd_group_remove(fgrp, reactor->events_fd);
1478 : 4046 : spdk_fd_group_remove(fgrp, reactor->resched_fd);
1479 : :
1480 : 4046 : close(reactor->events_fd);
1481 : 4046 : close(reactor->resched_fd);
1482 : :
1483 : 4046 : spdk_fd_group_destroy(fgrp);
1484 : 4046 : reactor->fgrp = NULL;
1485 : : }
1486 : :
1487 : : static struct spdk_governor *
1488 : 2351 : _governor_find(const char *name)
1489 : : {
1490 : : struct spdk_governor *governor, *tmp;
1491 : :
1492 [ + + ]: 2351 : TAILQ_FOREACH_SAFE(governor, &g_governor_list, link, tmp) {
1493 [ + + - + : 27 : if (strcmp(name, governor->name) == 0) {
+ - ]
1494 : 27 : return governor;
1495 : : }
1496 : : }
1497 : :
1498 : 2324 : return NULL;
1499 : : }
1500 : :
1501 : : int
1502 : 54 : spdk_governor_set(const char *name)
1503 : : {
1504 : : struct spdk_governor *governor;
1505 : 54 : int rc = 0;
1506 : :
1507 : : /* NULL governor was specifically requested */
1508 [ + + ]: 54 : if (name == NULL) {
1509 [ + + ]: 23 : if (g_governor) {
1510 : 3 : g_governor->deinit();
1511 : : }
1512 : 23 : g_governor = NULL;
1513 : 23 : return 0;
1514 : : }
1515 : :
1516 : 31 : governor = _governor_find(name);
1517 [ + + ]: 31 : if (governor == NULL) {
1518 : 4 : return -EINVAL;
1519 : : }
1520 : :
1521 [ - + ]: 27 : if (g_governor == governor) {
1522 : 0 : return 0;
1523 : : }
1524 : :
1525 : 27 : rc = governor->init();
1526 [ + + ]: 27 : if (rc == 0) {
1527 [ - + ]: 7 : if (g_governor) {
1528 : 0 : g_governor->deinit();
1529 : : }
1530 : 7 : g_governor = governor;
1531 : : }
1532 : :
1533 : 27 : return rc;
1534 : : }
1535 : :
1536 : : struct spdk_governor *
1537 : 1297 : spdk_governor_get(void)
1538 : : {
1539 : 1297 : return g_governor;
1540 : : }
1541 : :
1542 : : void
1543 : 2320 : spdk_governor_register(struct spdk_governor *governor)
1544 : : {
1545 [ - + ]: 2320 : if (_governor_find(governor->name)) {
1546 : 0 : SPDK_ERRLOG("governor named '%s' already registered.\n", governor->name);
1547 : 0 : assert(false);
1548 : : return;
1549 : : }
1550 : :
1551 : 2320 : TAILQ_INSERT_TAIL(&g_governor_list, governor, link);
1552 : : }
1553 : :
1554 : 2991 : SPDK_LOG_REGISTER_COMPONENT(reactor)
|