xref: /spdk/module/scheduler/dynamic/scheduler_dynamic.c (revision 462fd69e378d8990db838b5d6471ed2ed1ed9ec6)
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