1488570ebSJim Harris /* SPDX-License-Identifier: BSD-3-Clause 2a6dbe372Spaul luse * Copyright (C) 2021 Intel Corporation. 32bd631dcSJim Harris * All rights reserved. 42bd631dcSJim Harris */ 52bd631dcSJim Harris 62bd631dcSJim Harris #include "spdk/stdinc.h" 72bd631dcSJim Harris #include "spdk/likely.h" 82bd631dcSJim Harris #include "spdk/event.h" 92bd631dcSJim Harris #include "spdk/log.h" 102bd631dcSJim Harris #include "spdk/env.h" 112bd631dcSJim Harris 122bd631dcSJim Harris #include "spdk/thread.h" 132bd631dcSJim Harris #include "spdk_internal/event.h" 142bd631dcSJim Harris #include "spdk/scheduler.h" 154c5774fdSJim Harris #include "spdk_internal/usdt.h" 162bd631dcSJim Harris 172bd631dcSJim Harris static uint32_t g_main_lcore; 182bd631dcSJim Harris 192bd631dcSJim Harris struct core_stats { 202bd631dcSJim Harris uint64_t busy; 212bd631dcSJim Harris uint64_t idle; 222bd631dcSJim Harris uint32_t thread_count; 23*462fd69eSSeungYeon Shin bool isolated; 242bd631dcSJim Harris }; 252bd631dcSJim Harris 262bd631dcSJim Harris static struct core_stats *g_cores; 272bd631dcSJim Harris 28fcf5ae7eSKrzysztof Karas uint8_t g_scheduler_load_limit = 20; 29fcf5ae7eSKrzysztof Karas uint8_t g_scheduler_core_limit = 80; 30fcf5ae7eSKrzysztof Karas uint8_t g_scheduler_core_busy = 95; 312bd631dcSJim Harris 322bd631dcSJim Harris static uint8_t 33de04fa74SJim Harris _busy_pct(uint64_t busy, uint64_t idle) 34de04fa74SJim Harris { 35de04fa74SJim Harris if ((busy + idle) == 0) { 36de04fa74SJim Harris return 0; 37de04fa74SJim Harris } 38de04fa74SJim Harris 39de04fa74SJim Harris return busy * 100 / (busy + idle); 40de04fa74SJim Harris } 41de04fa74SJim Harris 42de04fa74SJim Harris static uint8_t 432bd631dcSJim Harris _get_thread_load(struct spdk_scheduler_thread_info *thread_info) 442bd631dcSJim Harris { 452bd631dcSJim Harris uint64_t busy, idle; 462bd631dcSJim Harris 472bd631dcSJim Harris busy = thread_info->current_stats.busy_tsc; 482bd631dcSJim Harris idle = thread_info->current_stats.idle_tsc; 492bd631dcSJim Harris 502bd631dcSJim Harris /* return percentage of time thread was busy */ 51de04fa74SJim Harris return _busy_pct(busy, idle); 522bd631dcSJim Harris } 532bd631dcSJim Harris 542bd631dcSJim Harris typedef void (*_foreach_fn)(struct spdk_scheduler_thread_info *thread_info); 552bd631dcSJim Harris 562bd631dcSJim Harris static void 572bd631dcSJim Harris _foreach_thread(struct spdk_scheduler_core_info *cores_info, _foreach_fn fn) 582bd631dcSJim Harris { 592bd631dcSJim Harris struct spdk_scheduler_core_info *core; 602bd631dcSJim Harris uint32_t i, j; 612bd631dcSJim Harris 622bd631dcSJim Harris SPDK_ENV_FOREACH_CORE(i) { 632bd631dcSJim Harris core = &cores_info[i]; 64*462fd69eSSeungYeon Shin /* Skip cores that are isolated */ 65*462fd69eSSeungYeon Shin if (core->isolated) { 66*462fd69eSSeungYeon Shin continue; 67*462fd69eSSeungYeon Shin } 682bd631dcSJim Harris for (j = 0; j < core->threads_count; j++) { 692bd631dcSJim Harris fn(&core->thread_infos[j]); 702bd631dcSJim Harris } 712bd631dcSJim Harris } 722bd631dcSJim Harris } 732bd631dcSJim Harris 742bd631dcSJim Harris static void 75a75a557bSJim Harris prepare_to_sleep(uint32_t core) 76a75a557bSJim Harris { 77a75a557bSJim Harris struct spdk_governor *governor = spdk_governor_get(); 78a75a557bSJim Harris int rc; 79a75a557bSJim Harris 80a75a557bSJim Harris if (governor == NULL) { 81a75a557bSJim Harris return; 82a75a557bSJim Harris } 83a75a557bSJim Harris 84a75a557bSJim Harris rc = governor->set_core_freq_min(core); 85a75a557bSJim Harris if (rc < 0) { 86a75a557bSJim Harris SPDK_ERRLOG("could not set_core_freq_min(%d)\n", core); 87a75a557bSJim Harris } 88a75a557bSJim Harris } 89a75a557bSJim Harris 90a75a557bSJim Harris static void 91a75a557bSJim Harris prepare_to_wake(uint32_t core) 92a75a557bSJim Harris { 93a75a557bSJim Harris struct spdk_governor *governor = spdk_governor_get(); 94a75a557bSJim Harris int rc; 95a75a557bSJim Harris 96a75a557bSJim Harris if (governor == NULL) { 97a75a557bSJim Harris return; 98a75a557bSJim Harris } 99a75a557bSJim Harris 100a75a557bSJim Harris rc = governor->set_core_freq_max(core); 101a75a557bSJim Harris if (rc < 0) { 102a75a557bSJim Harris SPDK_ERRLOG("could not set_core_freq_max(%d)\n", core); 103a75a557bSJim Harris } 104a75a557bSJim Harris } 105a75a557bSJim Harris 106a75a557bSJim Harris static void 1072bd631dcSJim Harris _move_thread(struct spdk_scheduler_thread_info *thread_info, uint32_t dst_core) 1082bd631dcSJim Harris { 1092bd631dcSJim Harris struct core_stats *dst = &g_cores[dst_core]; 1102bd631dcSJim Harris struct core_stats *src = &g_cores[thread_info->lcore]; 1112bd631dcSJim Harris uint64_t busy_tsc = thread_info->current_stats.busy_tsc; 112bcff0888SJim Harris uint8_t busy_pct = _busy_pct(src->busy, src->idle); 113bcff0888SJim Harris uint64_t tsc; 1142bd631dcSJim Harris 1154c5774fdSJim Harris SPDK_DTRACE_PROBE2(dynsched_move, thread_info, dst_core); 1164c5774fdSJim Harris 1172bd631dcSJim Harris if (src == dst) { 1182bd631dcSJim Harris /* Don't modify stats if thread is already on that core. */ 1192bd631dcSJim Harris return; 1202bd631dcSJim Harris } 1212bd631dcSJim Harris 1222bd631dcSJim Harris dst->busy += spdk_min(UINT64_MAX - dst->busy, busy_tsc); 1232bd631dcSJim Harris dst->idle -= spdk_min(dst->idle, busy_tsc); 1242bd631dcSJim Harris dst->thread_count++; 1252bd631dcSJim Harris 126f1acee8fSJim Harris /* Adjust busy/idle from core as if thread was not present on it. 127f1acee8fSJim Harris * Core load will reflect the sum of all remaining threads on it. */ 1282bd631dcSJim Harris src->busy -= spdk_min(src->busy, busy_tsc); 129f1acee8fSJim Harris src->idle += spdk_min(UINT64_MAX - src->idle, busy_tsc); 130f1acee8fSJim Harris 131fcf5ae7eSKrzysztof Karas if (busy_pct >= g_scheduler_core_busy && 132fcf5ae7eSKrzysztof Karas _busy_pct(src->busy, src->idle) < g_scheduler_core_limit) { 133bcff0888SJim Harris /* This core was so busy that we cannot assume all of busy_tsc 134bcff0888SJim Harris * consumed by the moved thread will now be idle_tsc - it's 135bcff0888SJim Harris * very possible the remaining threads will use these cycles 136bcff0888SJim Harris * as busy_tsc. 137bcff0888SJim Harris * 138bcff0888SJim Harris * So make sure we don't drop the updated estimate below 139fcf5ae7eSKrzysztof Karas * g_scheduler_core_limit, so that other cores can't 140bcff0888SJim Harris * move threads to this core during this scheduling 141bcff0888SJim Harris * period. 142bcff0888SJim Harris */ 143bcff0888SJim Harris tsc = src->busy + src->idle; 144fcf5ae7eSKrzysztof Karas src->busy = tsc * g_scheduler_core_limit / 100; 145bcff0888SJim Harris src->idle = tsc - src->busy; 146bcff0888SJim Harris } 1472bd631dcSJim Harris assert(src->thread_count > 0); 1482bd631dcSJim Harris src->thread_count--; 1492bd631dcSJim Harris 1502bd631dcSJim Harris thread_info->lcore = dst_core; 1512bd631dcSJim Harris } 1522bd631dcSJim Harris 1532bd631dcSJim Harris static bool 1543184f6e2SJim Harris _is_core_at_limit(uint32_t core_id) 1552bd631dcSJim Harris { 1562bd631dcSJim Harris struct core_stats *core = &g_cores[core_id]; 1572bd631dcSJim Harris uint64_t busy, idle; 1582bd631dcSJim Harris 1592bd631dcSJim Harris /* Core with no or single thread cannot be over the limit. */ 1602bd631dcSJim Harris if (core->thread_count <= 1) { 1612bd631dcSJim Harris return false; 1622bd631dcSJim Harris } 1632bd631dcSJim Harris 1642bd631dcSJim Harris busy = core->busy; 1652bd631dcSJim Harris idle = core->idle; 1662bd631dcSJim Harris 1672bd631dcSJim Harris /* No work was done, exit before possible division by 0. */ 1682bd631dcSJim Harris if (busy == 0) { 1692bd631dcSJim Harris return false; 1702bd631dcSJim Harris } 1712bd631dcSJim Harris 1722bd631dcSJim Harris /* Work done was less than the limit */ 173fcf5ae7eSKrzysztof Karas if (_busy_pct(busy, idle) < g_scheduler_core_limit) { 1742bd631dcSJim Harris return false; 1752bd631dcSJim Harris } 1762bd631dcSJim Harris 1772bd631dcSJim Harris return true; 1782bd631dcSJim Harris } 1792bd631dcSJim Harris 1802bd631dcSJim Harris static bool 1812bd631dcSJim Harris _can_core_fit_thread(struct spdk_scheduler_thread_info *thread_info, uint32_t dst_core) 1822bd631dcSJim Harris { 1832bd631dcSJim Harris struct core_stats *dst = &g_cores[dst_core]; 184cf494305SJim Harris uint64_t new_busy_tsc, new_idle_tsc; 1852bd631dcSJim Harris 1862bd631dcSJim Harris /* Thread can always fit on the core it's currently on. */ 1872bd631dcSJim Harris if (thread_info->lcore == dst_core) { 1882bd631dcSJim Harris return true; 1892bd631dcSJim Harris } 1902bd631dcSJim Harris 1912bd631dcSJim Harris /* Reactors in interrupt mode do not update stats, 1922bd631dcSJim Harris * a thread can always fit into reactor in interrupt mode. */ 1932bd631dcSJim Harris if (dst->busy + dst->idle == 0) { 1942bd631dcSJim Harris return true; 1952bd631dcSJim Harris } 1962bd631dcSJim Harris 1972bd631dcSJim Harris /* Core has no threads. */ 1982bd631dcSJim Harris if (dst->thread_count == 0) { 1992bd631dcSJim Harris return true; 2002bd631dcSJim Harris } 2012bd631dcSJim Harris 202cf494305SJim Harris /* Core doesn't have enough idle_tsc to take this thread. */ 203cf494305SJim Harris if (dst->idle < thread_info->current_stats.busy_tsc) { 2042bd631dcSJim Harris return false; 2052bd631dcSJim Harris } 2062bd631dcSJim Harris 207cf494305SJim Harris new_busy_tsc = dst->busy + thread_info->current_stats.busy_tsc; 208cf494305SJim Harris new_idle_tsc = dst->idle - thread_info->current_stats.busy_tsc; 209cf494305SJim Harris 210cf494305SJim Harris /* Core cannot fit this thread if it would put it over the 211fcf5ae7eSKrzysztof Karas * g_scheduler_core_limit. */ 212fcf5ae7eSKrzysztof Karas return _busy_pct(new_busy_tsc, new_idle_tsc) < g_scheduler_core_limit; 213cf494305SJim Harris } 214cf494305SJim Harris 2152bd631dcSJim Harris static uint32_t 2162bd631dcSJim Harris _find_optimal_core(struct spdk_scheduler_thread_info *thread_info) 2172bd631dcSJim Harris { 2182bd631dcSJim Harris uint32_t i; 2192bd631dcSJim Harris uint32_t current_lcore = thread_info->lcore; 2202bd631dcSJim Harris uint32_t least_busy_lcore = thread_info->lcore; 2212bd631dcSJim Harris struct spdk_thread *thread; 2222bd631dcSJim Harris struct spdk_cpuset *cpumask; 2233184f6e2SJim Harris bool core_at_limit = _is_core_at_limit(current_lcore); 2242bd631dcSJim Harris 2252bd631dcSJim Harris thread = spdk_thread_get_by_id(thread_info->thread_id); 2262bd631dcSJim Harris if (thread == NULL) { 2272bd631dcSJim Harris return current_lcore; 2282bd631dcSJim Harris } 2292bd631dcSJim Harris cpumask = spdk_thread_get_cpumask(thread); 2302bd631dcSJim Harris 2312bd631dcSJim Harris /* Find a core that can fit the thread. */ 2322bd631dcSJim Harris SPDK_ENV_FOREACH_CORE(i) { 2332bd631dcSJim Harris /* Ignore cores outside cpumask. */ 2342bd631dcSJim Harris if (!spdk_cpuset_get_cpu(cpumask, i)) { 2352bd631dcSJim Harris continue; 2362bd631dcSJim Harris } 2372bd631dcSJim Harris 238*462fd69eSSeungYeon Shin /* Skip cores that are isolated */ 239*462fd69eSSeungYeon Shin if (g_cores[i].isolated) { 240*462fd69eSSeungYeon Shin continue; 241*462fd69eSSeungYeon Shin } 242*462fd69eSSeungYeon Shin 2432bd631dcSJim Harris /* Search for least busy core. */ 2442bd631dcSJim Harris if (g_cores[i].busy < g_cores[least_busy_lcore].busy) { 2452bd631dcSJim Harris least_busy_lcore = i; 2462bd631dcSJim Harris } 2472bd631dcSJim Harris 2482bd631dcSJim Harris /* Skip cores that cannot fit the thread and current one. */ 2492bd631dcSJim Harris if (!_can_core_fit_thread(thread_info, i) || i == current_lcore) { 2502bd631dcSJim Harris continue; 2512bd631dcSJim Harris } 252f8a961f5STomasz Zawadzki if (i == g_main_lcore) { 253f8a961f5STomasz Zawadzki /* First consider g_main_lcore, consolidate threads on main lcore if possible. */ 254f8a961f5STomasz Zawadzki return i; 255f8a961f5STomasz Zawadzki } else if (i < current_lcore && current_lcore != g_main_lcore) { 2562bd631dcSJim Harris /* Lower core id was found, move to consolidate threads on lowest core ids. */ 2572bd631dcSJim Harris return i; 2583184f6e2SJim Harris } else if (core_at_limit) { 259f8a961f5STomasz Zawadzki /* When core is over the limit, any core id is better than current one. */ 2602bd631dcSJim Harris return i; 2612bd631dcSJim Harris } 2622bd631dcSJim Harris } 2632bd631dcSJim Harris 2642bd631dcSJim Harris /* For cores over the limit, place the thread on least busy core 2652bd631dcSJim Harris * to balance threads. */ 2663184f6e2SJim Harris if (core_at_limit) { 2672bd631dcSJim Harris return least_busy_lcore; 2682bd631dcSJim Harris } 2692bd631dcSJim Harris 2702bd631dcSJim Harris /* If no better core is found, remain on the same one. */ 2712bd631dcSJim Harris return current_lcore; 2722bd631dcSJim Harris } 2732bd631dcSJim Harris 2742bd631dcSJim Harris static int 2752bd631dcSJim Harris init(void) 2762bd631dcSJim Harris { 277*462fd69eSSeungYeon Shin g_main_lcore = spdk_scheduler_get_scheduling_lcore(); 2782bd631dcSJim Harris 2792bd631dcSJim Harris if (spdk_governor_set("dpdk_governor") != 0) { 2802bd631dcSJim Harris SPDK_NOTICELOG("Unable to initialize dpdk governor\n"); 2812bd631dcSJim Harris } 2822bd631dcSJim Harris 2832bd631dcSJim Harris g_cores = calloc(spdk_env_get_last_core() + 1, sizeof(struct core_stats)); 2842bd631dcSJim Harris if (g_cores == NULL) { 2852bd631dcSJim Harris SPDK_ERRLOG("Failed to allocate memory for dynamic scheduler core stats.\n"); 2862bd631dcSJim Harris return -ENOMEM; 2872bd631dcSJim Harris } 2882bd631dcSJim Harris 2892bd631dcSJim Harris return 0; 2902bd631dcSJim Harris } 2912bd631dcSJim Harris 2922bd631dcSJim Harris static void 2932bd631dcSJim Harris deinit(void) 2942bd631dcSJim Harris { 2952bd631dcSJim Harris free(g_cores); 2962bd631dcSJim Harris g_cores = NULL; 2972bd631dcSJim Harris spdk_governor_set(NULL); 2982bd631dcSJim Harris } 2992bd631dcSJim Harris 3002bd631dcSJim Harris static void 3012bd631dcSJim Harris _balance_idle(struct spdk_scheduler_thread_info *thread_info) 3022bd631dcSJim Harris { 303fcf5ae7eSKrzysztof Karas if (_get_thread_load(thread_info) >= g_scheduler_load_limit) { 3042bd631dcSJim Harris return; 3052bd631dcSJim Harris } 3062bd631dcSJim Harris /* This thread is idle, move it to the main core. */ 3072bd631dcSJim Harris _move_thread(thread_info, g_main_lcore); 3082bd631dcSJim Harris } 3092bd631dcSJim Harris 3102bd631dcSJim Harris static void 3112bd631dcSJim Harris _balance_active(struct spdk_scheduler_thread_info *thread_info) 3122bd631dcSJim Harris { 3132bd631dcSJim Harris uint32_t target_lcore; 3142bd631dcSJim Harris 315fcf5ae7eSKrzysztof Karas if (_get_thread_load(thread_info) < g_scheduler_load_limit) { 3162bd631dcSJim Harris return; 3172bd631dcSJim Harris } 3182bd631dcSJim Harris 3192bd631dcSJim Harris /* This thread is active. */ 3202bd631dcSJim Harris target_lcore = _find_optimal_core(thread_info); 3212bd631dcSJim Harris _move_thread(thread_info, target_lcore); 3222bd631dcSJim Harris } 3232bd631dcSJim Harris 3242bd631dcSJim Harris static void 3252bd631dcSJim Harris balance(struct spdk_scheduler_core_info *cores_info, uint32_t cores_count) 3262bd631dcSJim Harris { 3272bd631dcSJim Harris struct spdk_reactor *reactor; 3282bd631dcSJim Harris struct spdk_governor *governor; 3292bd631dcSJim Harris struct spdk_scheduler_core_info *core; 3302bd631dcSJim Harris struct core_stats *main_core; 3312bd631dcSJim Harris uint32_t i; 3322bd631dcSJim Harris int rc; 3332bd631dcSJim Harris bool busy_threads_present = false; 3342bd631dcSJim Harris 3354c5774fdSJim Harris SPDK_DTRACE_PROBE1(dynsched_balance, cores_count); 3364c5774fdSJim Harris 3372bd631dcSJim Harris SPDK_ENV_FOREACH_CORE(i) { 3382bd631dcSJim Harris g_cores[i].thread_count = cores_info[i].threads_count; 3392bd631dcSJim Harris g_cores[i].busy = cores_info[i].current_busy_tsc; 3402bd631dcSJim Harris g_cores[i].idle = cores_info[i].current_idle_tsc; 341*462fd69eSSeungYeon Shin g_cores[i].isolated = cores_info[i].isolated; 3424c5774fdSJim Harris SPDK_DTRACE_PROBE2(dynsched_core_info, i, &cores_info[i]); 3432bd631dcSJim Harris } 3442bd631dcSJim Harris main_core = &g_cores[g_main_lcore]; 3452bd631dcSJim Harris 3462bd631dcSJim Harris /* Distribute threads in two passes, to make sure updated core stats are considered on each pass. 3472bd631dcSJim Harris * 1) Move all idle threads to main core. */ 3482bd631dcSJim Harris _foreach_thread(cores_info, _balance_idle); 3492bd631dcSJim Harris /* 2) Distribute active threads across all cores. */ 3502bd631dcSJim Harris _foreach_thread(cores_info, _balance_active); 3512bd631dcSJim Harris 3522bd631dcSJim Harris /* Switch unused cores to interrupt mode and switch cores to polled mode 3532bd631dcSJim Harris * if they will be used after rebalancing */ 3542bd631dcSJim Harris SPDK_ENV_FOREACH_CORE(i) { 3552bd631dcSJim Harris reactor = spdk_reactor_get(i); 356f619c3b1SWojciech Panfil assert(reactor != NULL); 357f619c3b1SWojciech Panfil 3582bd631dcSJim Harris core = &cores_info[i]; 3592bd631dcSJim Harris /* We can switch mode only if reactor already does not have any threads */ 3602bd631dcSJim Harris if (g_cores[i].thread_count == 0 && TAILQ_EMPTY(&reactor->threads)) { 3612bd631dcSJim Harris core->interrupt_mode = true; 362a75a557bSJim Harris prepare_to_sleep(i); 3632bd631dcSJim Harris } else if (g_cores[i].thread_count != 0) { 3642bd631dcSJim Harris core->interrupt_mode = false; 3652bd631dcSJim Harris if (i != g_main_lcore) { 3662bd631dcSJim Harris /* If a thread is present on non g_main_lcore, 3672bd631dcSJim Harris * it has to be busy. */ 3682bd631dcSJim Harris busy_threads_present = true; 369a75a557bSJim Harris prepare_to_wake(i); 3702bd631dcSJim Harris } 3712bd631dcSJim Harris } 3722bd631dcSJim Harris } 3732bd631dcSJim Harris 3742bd631dcSJim Harris governor = spdk_governor_get(); 3752bd631dcSJim Harris if (governor == NULL) { 3762bd631dcSJim Harris return; 3772bd631dcSJim Harris } 3782bd631dcSJim Harris 3792bd631dcSJim Harris /* Change main core frequency if needed */ 3802bd631dcSJim Harris if (busy_threads_present) { 3812bd631dcSJim Harris rc = governor->set_core_freq_max(g_main_lcore); 3822bd631dcSJim Harris if (rc < 0) { 3832bd631dcSJim Harris SPDK_ERRLOG("setting default frequency for core %u failed\n", g_main_lcore); 3842bd631dcSJim Harris } 3852bd631dcSJim Harris } else if (main_core->busy > main_core->idle) { 3862bd631dcSJim Harris rc = governor->core_freq_up(g_main_lcore); 3872bd631dcSJim Harris if (rc < 0) { 3882bd631dcSJim Harris SPDK_ERRLOG("increasing frequency for core %u failed\n", g_main_lcore); 3892bd631dcSJim Harris } 3902bd631dcSJim Harris } else { 3912bd631dcSJim Harris rc = governor->core_freq_down(g_main_lcore); 3922bd631dcSJim Harris if (rc < 0) { 3932bd631dcSJim Harris SPDK_ERRLOG("lowering frequency for core %u failed\n", g_main_lcore); 3942bd631dcSJim Harris } 3952bd631dcSJim Harris } 3962bd631dcSJim Harris } 3972bd631dcSJim Harris 39832da7015SKrzysztof Karas struct json_scheduler_opts { 39932da7015SKrzysztof Karas uint8_t load_limit; 40032da7015SKrzysztof Karas uint8_t core_limit; 40132da7015SKrzysztof Karas uint8_t core_busy; 40232da7015SKrzysztof Karas }; 40332da7015SKrzysztof Karas 40432da7015SKrzysztof Karas static const struct spdk_json_object_decoder sched_decoders[] = { 40532da7015SKrzysztof Karas {"load_limit", offsetof(struct json_scheduler_opts, load_limit), spdk_json_decode_uint8, true}, 40632da7015SKrzysztof Karas {"core_limit", offsetof(struct json_scheduler_opts, core_limit), spdk_json_decode_uint8, true}, 40732da7015SKrzysztof Karas {"core_busy", offsetof(struct json_scheduler_opts, core_busy), spdk_json_decode_uint8, true}, 40832da7015SKrzysztof Karas }; 40932da7015SKrzysztof Karas 41032da7015SKrzysztof Karas static int 41132da7015SKrzysztof Karas set_opts(const struct spdk_json_val *opts) 41232da7015SKrzysztof Karas { 41332da7015SKrzysztof Karas struct json_scheduler_opts scheduler_opts; 41432da7015SKrzysztof Karas 41532da7015SKrzysztof Karas scheduler_opts.load_limit = g_scheduler_load_limit; 41632da7015SKrzysztof Karas scheduler_opts.core_limit = g_scheduler_core_limit; 41732da7015SKrzysztof Karas scheduler_opts.core_busy = g_scheduler_core_busy; 41832da7015SKrzysztof Karas 41932da7015SKrzysztof Karas if (opts != NULL) { 42032da7015SKrzysztof Karas if (spdk_json_decode_object_relaxed(opts, sched_decoders, 42132da7015SKrzysztof Karas SPDK_COUNTOF(sched_decoders), &scheduler_opts)) { 42232da7015SKrzysztof Karas SPDK_ERRLOG("Decoding scheduler opts JSON failed\n"); 42332da7015SKrzysztof Karas return -1; 42432da7015SKrzysztof Karas } 42532da7015SKrzysztof Karas } 42632da7015SKrzysztof Karas 42732da7015SKrzysztof Karas SPDK_NOTICELOG("Setting scheduler load limit to %d\n", scheduler_opts.load_limit); 42832da7015SKrzysztof Karas g_scheduler_load_limit = scheduler_opts.load_limit; 42932da7015SKrzysztof Karas SPDK_NOTICELOG("Setting scheduler core limit to %d\n", scheduler_opts.core_limit); 43032da7015SKrzysztof Karas g_scheduler_core_limit = scheduler_opts.core_limit; 43132da7015SKrzysztof Karas SPDK_NOTICELOG("Setting scheduler core busy to %d\n", scheduler_opts.core_busy); 43232da7015SKrzysztof Karas g_scheduler_core_busy = scheduler_opts.core_busy; 43332da7015SKrzysztof Karas 43432da7015SKrzysztof Karas return 0; 43532da7015SKrzysztof Karas } 43632da7015SKrzysztof Karas 43732da7015SKrzysztof Karas static void 43832da7015SKrzysztof Karas get_opts(struct spdk_json_write_ctx *ctx) 43932da7015SKrzysztof Karas { 44032da7015SKrzysztof Karas spdk_json_write_named_uint8(ctx, "load_limit", g_scheduler_load_limit); 44132da7015SKrzysztof Karas spdk_json_write_named_uint8(ctx, "core_limit", g_scheduler_core_limit); 44232da7015SKrzysztof Karas spdk_json_write_named_uint8(ctx, "core_busy", g_scheduler_core_busy); 44332da7015SKrzysztof Karas } 44432da7015SKrzysztof Karas 4452bd631dcSJim Harris static struct spdk_scheduler scheduler_dynamic = { 4462bd631dcSJim Harris .name = "dynamic", 4472bd631dcSJim Harris .init = init, 4482bd631dcSJim Harris .deinit = deinit, 4492bd631dcSJim Harris .balance = balance, 45032da7015SKrzysztof Karas .set_opts = set_opts, 45132da7015SKrzysztof Karas .get_opts = get_opts, 4522bd631dcSJim Harris }; 4532bd631dcSJim Harris 4542bd631dcSJim Harris SPDK_SCHEDULER_REGISTER(scheduler_dynamic); 455