xref: /spdk/lib/init/subsystem.c (revision 8afdeef3becfe9409cc9e7372bd0bc10e8b7d46d)
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
35 spdk_add_subsystem(struct spdk_subsystem *subsystem)
36 {
37 	TAILQ_INSERT_TAIL(&g_subsystems, subsystem, tailq);
38 }
39 
40 void
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 *
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 *
61 subsystem_find(const char *name)
62 {
63 	return _subsystem_find(&g_subsystems, name);
64 }
65 
66 bool
67 spdk_subsystem_exists(const char *name)
68 {
69 	return subsystem_find(name) != NULL;
70 }
71 
72 struct spdk_subsystem *
73 subsystem_get_first(void)
74 {
75 	return TAILQ_FIRST(&g_subsystems);
76 }
77 
78 struct spdk_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 *
86 subsystem_get_first_depend(void)
87 {
88 	return TAILQ_FIRST(&g_subsystems_deps);
89 }
90 
91 struct spdk_subsystem_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
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
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
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
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
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
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
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