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