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