xref: /spdk/module/scheduler/gscheduler/gscheduler.c (revision 83ba9086796471697a4975a58f60e2392bccd08c)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2021 Intel Corporation.
3  *   All rights reserved.
4  */
5 
6 #include "spdk/stdinc.h"
7 #include "spdk/likely.h"
8 
9 #include "spdk_internal/event.h"
10 #include "spdk/thread.h"
11 
12 #include "spdk/log.h"
13 #include "spdk/env.h"
14 #include "spdk/scheduler.h"
15 
16 static uint32_t g_max_threshold = 99;
17 static uint32_t g_adjust_threshold = 50;
18 static uint32_t g_min_threshold = 1;
19 
20 static int
21 init(void)
22 {
23 	return spdk_governor_set("dpdk_governor");
24 }
25 
26 static void
27 deinit(void)
28 {
29 	spdk_governor_set(NULL);
30 }
31 
32 static uint32_t
33 calculate_busy_pct(struct spdk_scheduler_core_info *core)
34 {
35 	uint64_t total_tsc;
36 
37 	total_tsc = core->current_busy_tsc + core->current_idle_tsc;
38 	if (total_tsc == 0) {
39 		return 0;
40 	} else {
41 		return core->current_busy_tsc * 100 / total_tsc;
42 	}
43 }
44 
45 struct check_sibling_ctx {
46 	struct spdk_scheduler_core_info *cores;
47 	uint32_t busy_pct;
48 };
49 
50 static void
51 check_sibling(void *_ctx, uint32_t i)
52 {
53 	struct check_sibling_ctx *ctx = _ctx;
54 	struct spdk_scheduler_core_info *core = &ctx->cores[i];
55 	uint32_t busy_pct = calculate_busy_pct(core);
56 
57 	ctx->busy_pct = spdk_max(ctx->busy_pct, busy_pct);
58 }
59 
60 static void
61 balance(struct spdk_scheduler_core_info *cores, uint32_t core_count)
62 {
63 	struct spdk_governor *governor;
64 	struct spdk_scheduler_core_info *core;
65 	struct spdk_governor_capabilities capabilities;
66 	uint32_t busy_pct;
67 	uint32_t i;
68 	int rc;
69 
70 	governor = spdk_governor_get();
71 	assert(governor != NULL);
72 
73 	/* Gather active/idle statistics */
74 	SPDK_ENV_FOREACH_CORE(i) {
75 		struct spdk_cpuset smt_siblings = {};
76 		core = &cores[i];
77 
78 		rc = governor->get_core_capabilities(core->lcore, &capabilities);
79 		if (rc < 0) {
80 			SPDK_ERRLOG("failed to get capabilities for core: %u\n", core->lcore);
81 			return;
82 		}
83 
84 		busy_pct = calculate_busy_pct(core);
85 		if (spdk_env_core_get_smt_cpuset(&smt_siblings, i)) {
86 			struct check_sibling_ctx ctx = {.cores = cores, .busy_pct = busy_pct};
87 
88 			spdk_cpuset_for_each_cpu(&smt_siblings, check_sibling, &ctx);
89 			busy_pct = ctx.busy_pct;
90 		}
91 
92 		if (busy_pct < g_min_threshold) {
93 			rc = governor->set_core_freq_min(core->lcore);
94 			if (rc < 0) {
95 				SPDK_ERRLOG("setting to minimal frequency for core %u failed\n", core->lcore);
96 			}
97 		} else if (busy_pct < g_adjust_threshold) {
98 			rc = governor->core_freq_down(core->lcore);
99 			if (rc < 0) {
100 				SPDK_ERRLOG("lowering frequency for core %u failed\n", core->lcore);
101 			}
102 		} else if (busy_pct >= g_max_threshold) {
103 			rc = governor->set_core_freq_max(core->lcore);
104 			if (rc < 0) {
105 				SPDK_ERRLOG("setting to maximal frequency for core %u failed\n", core->lcore);
106 			}
107 		} else {
108 			rc = governor->core_freq_up(core->lcore);
109 			if (rc < 0) {
110 				SPDK_ERRLOG("increasing frequency for core %u failed\n", core->lcore);
111 			}
112 		}
113 	}
114 }
115 
116 static struct spdk_scheduler gscheduler = {
117 	.name = "gscheduler",
118 	.init = init,
119 	.deinit = deinit,
120 	.balance = balance,
121 };
122 
123 SPDK_SCHEDULER_REGISTER(gscheduler);
124