From 11c9b3960b78f6f61ff9061b0b0a55e4a7bedce8 Mon Sep 17 00:00:00 2001 From: Tomasz Zawadzki Date: Thu, 10 Jun 2021 08:44:08 -0400 Subject: [PATCH] scheduler_dynamic: move thread to least busy core In cases when all cores are already doing too much work to fit a thread, active threads should still be balanced over all cores. When current core is overloaded, place the thread on another that is less busy. The core limit is set to 95% to catch only ones that are fully busy. Decreasing that value would make spreading out the threads move aggressive. Changed thread load in one of the unit tests to reflect the 95% limit. Signed-off-by: Tomasz Zawadzki Change-Id: I3b3bc5f7fbd22725441fa811d61446950000cc46 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/8113 Community-CI: Broadcom CI Community-CI: Mellanox Build Bot Tested-by: SPDK CI Jenkins Reviewed-by: Paul Luse Reviewed-by: Krzysztof Karas Reviewed-by: Shuhei Matsumoto Reviewed-by: Ben Walker Reviewed-by: Maciej Szwed Reviewed-by: Konrad Sztyber Reviewed-by: Jim Harris --- lib/event/scheduler_dynamic.c | 40 ++++++++++++++++++++++ test/unit/lib/event/reactor.c/reactor_ut.c | 2 +- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/lib/event/scheduler_dynamic.c b/lib/event/scheduler_dynamic.c index 463aae7d8..f71151e93 100644 --- a/lib/event/scheduler_dynamic.c +++ b/lib/event/scheduler_dynamic.c @@ -54,6 +54,7 @@ static struct core_stats *g_cores; #define SCHEDULER_THREAD_BUSY 100 #define SCHEDULER_LOAD_LIMIT 50 +#define SCHEDULER_CORE_LIMIT 95 static uint32_t _get_next_target_core(void) @@ -126,6 +127,33 @@ _move_thread(struct spdk_lw_thread *lw_thread, uint32_t dst_core) lw_thread->lcore = dst_core; } +static bool +_is_core_over_limit(uint32_t core_id) +{ + struct core_stats *core = &g_cores[core_id]; + uint64_t busy, idle; + + /* Core with no or single thread cannot be over the limit. */ + if (core->thread_count <= 1) { + return false; + } + + busy = core->busy; + idle = core->idle; + + /* No work was done, exit before possible division by 0. */ + if (busy == 0) { + return false; + } + + /* Work done was less than the limit */ + if (busy * 100 / (busy + idle) < SCHEDULER_CORE_LIMIT) { + return false; + } + + return true; +} + static bool _can_core_fit_thread(struct spdk_lw_thread *lw_thread, uint32_t dst_core) { @@ -159,6 +187,7 @@ _find_optimal_core(struct spdk_lw_thread *lw_thread) uint32_t i; uint32_t target_lcore; uint32_t current_lcore = lw_thread->lcore; + uint32_t least_busy_lcore = lw_thread->lcore; struct spdk_thread *thread = spdk_thread_get_from_ctx(lw_thread); struct spdk_cpuset *cpumask = spdk_thread_get_cpumask(thread); @@ -171,6 +200,11 @@ _find_optimal_core(struct spdk_lw_thread *lw_thread) continue; } + /* Search for least busy core. */ + if (g_cores[target_lcore].busy < g_cores[least_busy_lcore].busy) { + least_busy_lcore = target_lcore; + } + /* Skip cores that cannot fit the thread and current one. */ if (!_can_core_fit_thread(lw_thread, target_lcore) || target_lcore == current_lcore) { continue; @@ -179,6 +213,12 @@ _find_optimal_core(struct spdk_lw_thread *lw_thread) return target_lcore; } + /* For cores over the limit, place the thread on least busy core + * to balance threads. */ + if (_is_core_over_limit(current_lcore)) { + return least_busy_lcore; + } + /* If no better core is found, remain on the same one. */ return current_lcore; } diff --git a/test/unit/lib/event/reactor.c/reactor_ut.c b/test/unit/lib/event/reactor.c/reactor_ut.c index 0753d3ec4..5c8f213c4 100644 --- a/test/unit/lib/event/reactor.c/reactor_ut.c +++ b/test/unit/lib/event/reactor.c/reactor_ut.c @@ -894,7 +894,7 @@ test_governor(void) /* TEST 3 */ /* Make second thread very busy so that it will be moved to second core */ spdk_set_thread(thread[1]); - busy = spdk_poller_register(poller_run_busy, (void *)1000, 0); + busy = spdk_poller_register(poller_run_busy, (void *)2000, 0); _reactor_run(reactor); spdk_poller_unregister(&busy);