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