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