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