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
8 #include "spdk/init.h"
9 #include "spdk/log.h"
10 #include "spdk/queue.h"
11 #include "spdk/thread.h"
12
13 #include "spdk_internal/init.h"
14 #include "spdk/env.h"
15
16 #include "spdk/json.h"
17
18 #include "subsystem.h"
19
20 TAILQ_HEAD(spdk_subsystem_list, spdk_subsystem);
21 struct spdk_subsystem_list g_subsystems = TAILQ_HEAD_INITIALIZER(g_subsystems);
22
23 TAILQ_HEAD(spdk_subsystem_depend_list, spdk_subsystem_depend);
24 struct spdk_subsystem_depend_list g_subsystems_deps = TAILQ_HEAD_INITIALIZER(g_subsystems_deps);
25 static struct spdk_subsystem *g_next_subsystem;
26 static bool g_subsystems_initialized = false;
27 static bool g_subsystems_init_interrupted = false;
28 static spdk_subsystem_init_fn g_subsystem_start_fn = NULL;
29 static void *g_subsystem_start_arg = NULL;
30 static spdk_msg_fn g_subsystem_stop_fn = NULL;
31 static void *g_subsystem_stop_arg = NULL;
32 static struct spdk_thread *g_fini_thread = NULL;
33
34 void
spdk_add_subsystem(struct spdk_subsystem * subsystem)35 spdk_add_subsystem(struct spdk_subsystem *subsystem)
36 {
37 TAILQ_INSERT_TAIL(&g_subsystems, subsystem, tailq);
38 }
39
40 void
spdk_add_subsystem_depend(struct spdk_subsystem_depend * depend)41 spdk_add_subsystem_depend(struct spdk_subsystem_depend *depend)
42 {
43 TAILQ_INSERT_TAIL(&g_subsystems_deps, depend, tailq);
44 }
45
46 static struct spdk_subsystem *
_subsystem_find(struct spdk_subsystem_list * list,const char * name)47 _subsystem_find(struct spdk_subsystem_list *list, const char *name)
48 {
49 struct spdk_subsystem *iter;
50
51 TAILQ_FOREACH(iter, list, tailq) {
52 if (strcmp(name, iter->name) == 0) {
53 return iter;
54 }
55 }
56
57 return NULL;
58 }
59
60 struct spdk_subsystem *
subsystem_find(const char * name)61 subsystem_find(const char *name)
62 {
63 return _subsystem_find(&g_subsystems, name);
64 }
65
66 bool
spdk_subsystem_exists(const char * name)67 spdk_subsystem_exists(const char *name)
68 {
69 return subsystem_find(name) != NULL;
70 }
71
72 struct spdk_subsystem *
subsystem_get_first(void)73 subsystem_get_first(void)
74 {
75 return TAILQ_FIRST(&g_subsystems);
76 }
77
78 struct spdk_subsystem *
subsystem_get_next(struct spdk_subsystem * cur_subsystem)79 subsystem_get_next(struct spdk_subsystem *cur_subsystem)
80 {
81 return TAILQ_NEXT(cur_subsystem, tailq);
82 }
83
84
85 struct spdk_subsystem_depend *
subsystem_get_first_depend(void)86 subsystem_get_first_depend(void)
87 {
88 return TAILQ_FIRST(&g_subsystems_deps);
89 }
90
91 struct spdk_subsystem_depend *
subsystem_get_next_depend(struct spdk_subsystem_depend * cur_depend)92 subsystem_get_next_depend(struct spdk_subsystem_depend *cur_depend)
93 {
94 return TAILQ_NEXT(cur_depend, tailq);
95 }
96
97 static void
subsystem_sort(void)98 subsystem_sort(void)
99 {
100 bool has_dependency, all_dependencies_met;
101 struct spdk_subsystem *subsystem, *subsystem_tmp;
102 struct spdk_subsystem_depend *subsystem_dep;
103 struct spdk_subsystem_list sorted_list;
104
105 TAILQ_INIT(&sorted_list);
106 /* We will move subsystems from the original g_subsystems TAILQ to the temporary
107 * sorted_list one at a time. We can only move a subsystem if it either (a) has no
108 * dependencies, or (b) all of its dependencies have already been moved to the
109 * sorted_list.
110 *
111 * Once all of the subsystems have been moved to the temporary list, we will move
112 * the list as-is back to the original g_subsystems TAILQ - they will now be sorted
113 * in the order which they must be initialized.
114 */
115 while (!TAILQ_EMPTY(&g_subsystems)) {
116 TAILQ_FOREACH_SAFE(subsystem, &g_subsystems, tailq, subsystem_tmp) {
117 has_dependency = false;
118 all_dependencies_met = true;
119 TAILQ_FOREACH(subsystem_dep, &g_subsystems_deps, tailq) {
120 if (strcmp(subsystem->name, subsystem_dep->name) == 0) {
121 has_dependency = true;
122 if (!_subsystem_find(&sorted_list, subsystem_dep->depends_on)) {
123 /* We found a dependency that isn't in the sorted_list yet.
124 * Clear the flag and break from the inner loop, we know
125 * we can't move this subsystem to the sorted_list yet.
126 */
127 all_dependencies_met = false;
128 break;
129 }
130 }
131 }
132
133 if (!has_dependency || all_dependencies_met) {
134 TAILQ_REMOVE(&g_subsystems, subsystem, tailq);
135 TAILQ_INSERT_TAIL(&sorted_list, subsystem, tailq);
136 }
137 }
138 }
139
140 TAILQ_SWAP(&sorted_list, &g_subsystems, spdk_subsystem, tailq);
141 }
142
143 void
spdk_subsystem_init_next(int rc)144 spdk_subsystem_init_next(int rc)
145 {
146 assert(spdk_thread_is_app_thread(NULL));
147
148 /* The initialization is interrupted by the spdk_subsystem_fini, so just return */
149 if (g_subsystems_init_interrupted) {
150 return;
151 }
152
153 if (rc) {
154 SPDK_ERRLOG("Init subsystem %s failed\n", g_next_subsystem->name);
155 g_subsystem_start_fn(rc, g_subsystem_start_arg);
156 return;
157 }
158
159 if (!g_next_subsystem) {
160 g_next_subsystem = TAILQ_FIRST(&g_subsystems);
161 } else {
162 g_next_subsystem = TAILQ_NEXT(g_next_subsystem, tailq);
163 }
164
165 if (!g_next_subsystem) {
166 g_subsystems_initialized = true;
167 g_subsystem_start_fn(0, g_subsystem_start_arg);
168 return;
169 }
170
171 if (g_next_subsystem->init) {
172 g_next_subsystem->init();
173 } else {
174 spdk_subsystem_init_next(0);
175 }
176 }
177
178 void
spdk_subsystem_init(spdk_subsystem_init_fn cb_fn,void * cb_arg)179 spdk_subsystem_init(spdk_subsystem_init_fn cb_fn, void *cb_arg)
180 {
181 struct spdk_subsystem_depend *dep;
182
183 assert(spdk_thread_is_app_thread(NULL));
184
185 g_subsystem_start_fn = cb_fn;
186 g_subsystem_start_arg = cb_arg;
187
188 /* Verify that all dependency name and depends_on subsystems are registered */
189 TAILQ_FOREACH(dep, &g_subsystems_deps, tailq) {
190 if (!subsystem_find(dep->name)) {
191 SPDK_ERRLOG("subsystem %s is missing\n", dep->name);
192 g_subsystem_start_fn(-1, g_subsystem_start_arg);
193 return;
194 }
195 if (!subsystem_find(dep->depends_on)) {
196 SPDK_ERRLOG("subsystem %s dependency %s is missing\n",
197 dep->name, dep->depends_on);
198 g_subsystem_start_fn(-1, g_subsystem_start_arg);
199 return;
200 }
201 }
202
203 subsystem_sort();
204
205 spdk_subsystem_init_next(0);
206 }
207
208 static void
subsystem_fini_next(void * arg1)209 subsystem_fini_next(void *arg1)
210 {
211 assert(g_fini_thread == spdk_get_thread());
212
213 if (!g_next_subsystem) {
214 /* If the initialized flag is false, then we've failed to initialize
215 * the very first subsystem and no de-init is needed
216 */
217 if (g_subsystems_initialized) {
218 g_next_subsystem = TAILQ_LAST(&g_subsystems, spdk_subsystem_list);
219 }
220 } else {
221 if (g_subsystems_initialized || g_subsystems_init_interrupted) {
222 g_next_subsystem = TAILQ_PREV(g_next_subsystem, spdk_subsystem_list, tailq);
223 } else {
224 g_subsystems_init_interrupted = true;
225 }
226 }
227
228 while (g_next_subsystem) {
229 if (g_next_subsystem->fini) {
230 g_next_subsystem->fini();
231 return;
232 }
233 g_next_subsystem = TAILQ_PREV(g_next_subsystem, spdk_subsystem_list, tailq);
234 }
235
236 g_subsystem_stop_fn(g_subsystem_stop_arg);
237 return;
238 }
239
240 void
spdk_subsystem_fini_next(void)241 spdk_subsystem_fini_next(void)
242 {
243 if (g_fini_thread != spdk_get_thread()) {
244 spdk_thread_send_msg(g_fini_thread, subsystem_fini_next, NULL);
245 } else {
246 subsystem_fini_next(NULL);
247 }
248 }
249
250 void
spdk_subsystem_fini(spdk_msg_fn cb_fn,void * cb_arg)251 spdk_subsystem_fini(spdk_msg_fn cb_fn, void *cb_arg)
252 {
253 g_subsystem_stop_fn = cb_fn;
254 g_subsystem_stop_arg = cb_arg;
255
256 g_fini_thread = spdk_get_thread();
257
258 spdk_subsystem_fini_next();
259 }
260
261 void
subsystem_config_json(struct spdk_json_write_ctx * w,struct spdk_subsystem * subsystem)262 subsystem_config_json(struct spdk_json_write_ctx *w, struct spdk_subsystem *subsystem)
263 {
264 if (subsystem && subsystem->write_config_json) {
265 subsystem->write_config_json(w);
266 } else {
267 spdk_json_write_null(w);
268 }
269 }
270