1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2020 Intel Corporation. 3 * Copyright (C) 2024 Samsung Electronics Co., Ltd. 4 * All rights reserved. 5 */ 6 7 #include "spdk/stdinc.h" 8 #include "spdk/likely.h" 9 #include "spdk/event.h" 10 #include "spdk/log.h" 11 #include "spdk/env.h" 12 #include "spdk/string.h" 13 #include "spdk/scheduler.h" 14 15 #include "spdk_internal/event.h" 16 #include "event_internal.h" 17 18 static bool g_first_load = true; 19 20 static int 21 init_static(void) 22 { 23 if (g_first_load) { 24 /* There is no scheduling performed by static scheduler, 25 * do not set the scheduling period. */ 26 spdk_scheduler_set_period(0); 27 } else { 28 /* Schedule a balance to happen immediately, so that 29 * we can reset each thread's lcore back to its original 30 * state. 31 */ 32 spdk_scheduler_set_period(1); 33 } 34 35 return 0; 36 } 37 38 static void 39 deinit_static(void) 40 { 41 g_first_load = false; 42 } 43 44 static void 45 balance_static(struct spdk_scheduler_core_info *cores, uint32_t core_count) 46 { 47 struct spdk_scheduler_core_info *core_info; 48 struct spdk_scheduler_thread_info *thread_info; 49 struct spdk_lw_thread *lw_thread; 50 struct spdk_thread *thread; 51 uint32_t i, j; 52 53 for (i = 0; i < core_count; i++) { 54 core_info = &cores[i]; 55 core_info->interrupt_mode = false; 56 for (j = 0; j < core_info->threads_count; j++) { 57 thread_info = &core_info->thread_infos[j]; 58 thread = spdk_thread_get_by_id(thread_info->thread_id); 59 lw_thread = spdk_thread_get_ctx(thread); 60 thread_info->lcore = lw_thread->initial_lcore; 61 } 62 } 63 64 /* We've restored the original state now, so we don't need to 65 * balance() anymore. 66 */ 67 spdk_scheduler_set_period(0); 68 } 69 70 static const struct spdk_json_object_decoder static_sched_decoders[] = { 71 {"mappings", 0, spdk_json_decode_string, true}, 72 }; 73 74 static int 75 set_opts_static(const struct spdk_json_val *opts) 76 { 77 char *tok, *mappings = NULL, *copy; 78 bool valid; 79 80 if (opts != NULL) { 81 if (spdk_json_decode_object_relaxed(opts, static_sched_decoders, 82 SPDK_COUNTOF(static_sched_decoders), 83 &mappings)) { 84 SPDK_ERRLOG("Decoding scheduler opts JSON failed\n"); 85 return -EINVAL; 86 } 87 } 88 89 if (mappings == NULL) { 90 return 0; 91 } 92 93 copy = strdup(mappings); 94 if (copy == NULL) { 95 free(mappings); 96 return -ENOMEM; 97 } 98 99 valid = true; 100 tok = strtok(mappings, ":"); 101 while (tok) { 102 struct spdk_lw_thread *lw_thread = NULL; 103 struct spdk_thread *thread; 104 int thread_id, core; 105 106 thread_id = spdk_strtol(tok, 10); 107 if (thread_id > 0) { 108 thread = spdk_thread_get_by_id(thread_id); 109 if (thread != NULL) { 110 lw_thread = spdk_thread_get_ctx(thread); 111 } 112 } 113 if (lw_thread == NULL) { 114 SPDK_ERRLOG("invalid thread ID '%s' in mappings '%s'\n", tok, copy); 115 valid = false; 116 break; 117 } 118 119 tok = strtok(NULL, ","); 120 core = spdk_strtol(tok, 10); 121 if (core < 0 || spdk_reactor_get(core) == NULL) { 122 SPDK_ERRLOG("invalid core number '%s' in mappings '%s'\n", tok, copy); 123 valid = false; 124 break; 125 } 126 127 if (!spdk_cpuset_get_cpu(spdk_thread_get_cpumask(thread), core)) { 128 SPDK_ERRLOG("core %d not in thread %d cpumask\n", core, thread_id); 129 valid = false; 130 break; 131 } 132 133 tok = strtok(NULL, ":"); 134 } 135 free(mappings); 136 if (!valid) { 137 free(copy); 138 return -EINVAL; 139 } 140 141 tok = strtok(copy, ":"); 142 while (tok) { 143 struct spdk_lw_thread *lw_thread = NULL; 144 struct spdk_thread *thread; 145 int thread_id, core; 146 147 thread_id = spdk_strtol(tok, 10); 148 thread = spdk_thread_get_by_id(thread_id); 149 lw_thread = spdk_thread_get_ctx(thread); 150 tok = strtok(NULL, ","); 151 core = spdk_strtol(tok, 10); 152 /* initial_lcore saves the static scheduler's lcore mapping. 153 * This is used to restore the previous mapping if we 154 * change to another scheduler and then back. So we can just 155 * change the ->initial_lcore here and kick the scheduler to 156 * put the new mapping into effect. 157 */ 158 lw_thread->initial_lcore = core; 159 tok = strtok(NULL, ":"); 160 } 161 free(copy); 162 163 /* We have updated some core placements, so kick the scheduler to 164 * apply those new placements. 165 */ 166 spdk_scheduler_set_period(1); 167 return 0; 168 } 169 170 static struct spdk_scheduler scheduler = { 171 .name = "static", 172 .init = init_static, 173 .deinit = deinit_static, 174 .balance = balance_static, 175 .set_opts = set_opts_static, 176 }; 177 SPDK_SCHEDULER_REGISTER(scheduler); 178