xref: /spdk/lib/event/scheduler_static.c (revision 83ba9086796471697a4975a58f60e2392bccd08c)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2020 Intel Corporation.
3  *   Copyright (C) 2024 Samsung Electronics Co., Ltd.
4  *   All rights reserved.
5  */
6 
7 #include "spdk/stdinc.h"
8 #include "spdk/likely.h"
9 #include "spdk/event.h"
10 #include "spdk/log.h"
11 #include "spdk/env.h"
12 #include "spdk/string.h"
13 #include "spdk/scheduler.h"
14 
15 #include "spdk_internal/event.h"
16 #include "event_internal.h"
17 
18 static bool g_first_load = true;
19 
20 static int
21 init_static(void)
22 {
23 	if (g_first_load) {
24 		/* There is no scheduling performed by static scheduler,
25 		 * do not set the scheduling period. */
26 		spdk_scheduler_set_period(0);
27 	} else {
28 		/* Schedule a balance to happen immediately, so that
29 		 * we can reset each thread's lcore back to its original
30 		 * state.
31 		 */
32 		spdk_scheduler_set_period(1);
33 	}
34 
35 	return 0;
36 }
37 
38 static void
39 deinit_static(void)
40 {
41 	g_first_load = false;
42 }
43 
44 static void
45 balance_static(struct spdk_scheduler_core_info *cores, uint32_t core_count)
46 {
47 	struct spdk_scheduler_core_info *core_info;
48 	struct spdk_scheduler_thread_info *thread_info;
49 	struct spdk_lw_thread *lw_thread;
50 	struct spdk_thread *thread;
51 	uint32_t i, j;
52 
53 	for (i = 0; i < core_count; i++) {
54 		core_info = &cores[i];
55 		core_info->interrupt_mode = false;
56 		for (j = 0; j < core_info->threads_count; j++) {
57 			thread_info = &core_info->thread_infos[j];
58 			thread = spdk_thread_get_by_id(thread_info->thread_id);
59 			lw_thread = spdk_thread_get_ctx(thread);
60 			thread_info->lcore = lw_thread->initial_lcore;
61 		}
62 	}
63 
64 	/* We've restored the original state now, so we don't need to
65 	 * balance() anymore.
66 	 */
67 	spdk_scheduler_set_period(0);
68 }
69 
70 static const struct spdk_json_object_decoder static_sched_decoders[] = {
71 	{"mappings", 0, spdk_json_decode_string, true},
72 };
73 
74 static int
75 set_opts_static(const struct spdk_json_val *opts)
76 {
77 	char *tok, *mappings = NULL, *copy;
78 	bool valid;
79 
80 	if (opts != NULL) {
81 		if (spdk_json_decode_object_relaxed(opts, static_sched_decoders,
82 						    SPDK_COUNTOF(static_sched_decoders),
83 						    &mappings)) {
84 			SPDK_ERRLOG("Decoding scheduler opts JSON failed\n");
85 			return -EINVAL;
86 		}
87 	}
88 
89 	if (mappings == NULL) {
90 		return 0;
91 	}
92 
93 	copy = strdup(mappings);
94 	if (copy == NULL) {
95 		free(mappings);
96 		return -ENOMEM;
97 	}
98 
99 	valid = true;
100 	tok = strtok(mappings, ":");
101 	while (tok) {
102 		struct spdk_lw_thread *lw_thread = NULL;
103 		struct spdk_thread *thread;
104 		int thread_id, core;
105 
106 		thread_id = spdk_strtol(tok, 10);
107 		if (thread_id > 0) {
108 			thread = spdk_thread_get_by_id(thread_id);
109 			if (thread != NULL) {
110 				lw_thread = spdk_thread_get_ctx(thread);
111 			}
112 		}
113 		if (lw_thread == NULL) {
114 			SPDK_ERRLOG("invalid thread ID '%s' in mappings '%s'\n", tok, copy);
115 			valid = false;
116 			break;
117 		}
118 
119 		tok = strtok(NULL, ",");
120 		core = spdk_strtol(tok, 10);
121 		if (core < 0 || spdk_reactor_get(core) == NULL) {
122 			SPDK_ERRLOG("invalid core number '%s' in mappings '%s'\n", tok, copy);
123 			valid = false;
124 			break;
125 		}
126 
127 		if (!spdk_cpuset_get_cpu(spdk_thread_get_cpumask(thread), core)) {
128 			SPDK_ERRLOG("core %d not in thread %d cpumask\n", core, thread_id);
129 			valid = false;
130 			break;
131 		}
132 
133 		tok = strtok(NULL, ":");
134 	}
135 	free(mappings);
136 	if (!valid) {
137 		free(copy);
138 		return -EINVAL;
139 	}
140 
141 	tok = strtok(copy, ":");
142 	while (tok) {
143 		struct spdk_lw_thread *lw_thread = NULL;
144 		struct spdk_thread *thread;
145 		int thread_id, core;
146 
147 		thread_id = spdk_strtol(tok, 10);
148 		thread = spdk_thread_get_by_id(thread_id);
149 		lw_thread = spdk_thread_get_ctx(thread);
150 		tok = strtok(NULL, ",");
151 		core = spdk_strtol(tok, 10);
152 		/* initial_lcore saves the static scheduler's lcore mapping.
153 		 * This is used to restore the previous mapping if we
154 		 * change to another scheduler and then back. So we can just
155 		 * change the ->initial_lcore here and kick the scheduler to
156 		 * put the new mapping into effect.
157 		 */
158 		lw_thread->initial_lcore = core;
159 		tok = strtok(NULL, ":");
160 	}
161 	free(copy);
162 
163 	/* We have updated some core placements, so kick the scheduler to
164 	 * apply those new placements.
165 	 */
166 	spdk_scheduler_set_period(1);
167 	return 0;
168 }
169 
170 static struct spdk_scheduler scheduler = {
171 	.name = "static",
172 	.init = init_static,
173 	.deinit = deinit_static,
174 	.balance = balance_static,
175 	.set_opts = set_opts_static,
176 };
177 SPDK_SCHEDULER_REGISTER(scheduler);
178