diff options
-rw-r--r-- | erts/doc/src/erl.xml | 21 | ||||
-rw-r--r-- | erts/doc/src/erlang.xml | 14 | ||||
-rw-r--r-- | erts/emulator/beam/erl_bif_info.c | 16 | ||||
-rw-r--r-- | erts/emulator/beam/erl_cpu_topology.c | 13 | ||||
-rw-r--r-- | erts/emulator/beam/erl_cpu_topology.h | 5 | ||||
-rw-r--r-- | erts/emulator/beam/erl_init.c | 19 | ||||
-rw-r--r-- | erts/emulator/test/scheduler_SUITE.erl | 191 | ||||
-rw-r--r-- | erts/include/internal/erl_misc_utils.h | 1 | ||||
-rw-r--r-- | erts/lib_src/common/erl_misc_utils.c | 255 |
9 files changed, 434 insertions, 101 deletions
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index ed1b0880b4..7e34e10a96 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -944,14 +944,19 @@ <c><![CDATA[+S Schedulers:SchedulerOnline]]></c></tag> <item> <p>Sets the number of scheduler threads to create and scheduler threads - to set online. The maximum for both - values is 1024. If the Erlang runtime system is able to determine the - number of logical processors configured and logical processors - available, <c>Schedulers</c> defaults to logical processors - configured, and <c>SchedulersOnline</c> defaults to logical processors - available; otherwise the default values are 1. <c>Schedulers</c> can - be omitted if <c>:SchedulerOnline</c> is not and conversely. The - number of schedulers online can be changed at runtime through + to set online. The maximum for both values is 1024. If the Erlang + runtime system is able to determine the number of logical processors + configured and logical processors available, <c>Schedulers</c> + defaults to logical processors configured, and + <c>SchedulersOnline</c> defaults to logical processors available; + otherwise the default values are 1. If the emulator detects that it + is subject to a <seealso marker="erlang#system_info_cpu_quota">CPU + quota</seealso>, the default value for <c>SchedulersOnline</c> will + be limited accordingly.</p> + <p> + <c>Schedulers</c> can be omitted if <c>:SchedulerOnline</c> is not + and conversely. The number of schedulers online can be changed at + runtime through <seealso marker="erlang#system_flag_schedulers_online"> <c>erlang:system_flag(schedulers_online, SchedulersOnline)</c></seealso>.</p> diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 2183f75487..bc967d4554 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -8153,6 +8153,15 @@ Metadata = #{ pid => pid(), <seealso marker="#system_info_logical_processors">logical processors configured</seealso>.</p> </item> + <tag><marker id="system_info_cpu_quota"/> + <c>cpu_quota</c></tag> + <item> + <p>Returns the detected CPU quota the emulator is limited by. The + return value is an integer saying how many processors' worth of + runtime we get (between 1 and the number of logical processors), + or the atom <c>unknown</c> if the emulator cannot detect a + quota.</p> + </item> <tag><marker id="system_info_update_cpu_info"/> <c>update_cpu_info</c></tag> <item> @@ -8162,8 +8171,9 @@ Metadata = #{ pid => pid(), CPU topology</seealso> and the number of logical processors <seealso marker="#system_info_logical_processors">configured</seealso>, <seealso marker="#system_info_logical_processors_online">online</seealso>, - and <seealso marker="#system_info_logical_processors_available"> - available</seealso>.</p> + <seealso marker="#system_info_logical_processors_available">available</seealso>, + and <seealso marker="#system_info_cpu_quota">cpu + quota</seealso>.</p> <p>If the CPU information has changed since the last time it was read, the atom <c>changed</c> is returned, otherwise the atom <c>unchanged</c>. If the CPU information has changed, diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index b06b5fc1ab..9483e12522 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2825,7 +2825,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) /* Arguments that are unusual follow ... */ else if (ERTS_IS_ATOM_STR("logical_processors", BIF_ARG_1)) { int no; - erts_get_logical_processors(&no, NULL, NULL); + erts_get_logical_processors(&no, NULL, NULL, NULL); if (no > 0) BIF_RET(make_small((Uint) no)); else { @@ -2835,7 +2835,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) } else if (ERTS_IS_ATOM_STR("logical_processors_online", BIF_ARG_1)) { int no; - erts_get_logical_processors(NULL, &no, NULL); + erts_get_logical_processors(NULL, &no, NULL, NULL); if (no > 0) BIF_RET(make_small((Uint) no)); else { @@ -2845,7 +2845,17 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) } else if (ERTS_IS_ATOM_STR("logical_processors_available", BIF_ARG_1)) { int no; - erts_get_logical_processors(NULL, NULL, &no); + erts_get_logical_processors(NULL, NULL, &no, NULL); + if (no > 0) + BIF_RET(make_small((Uint) no)); + else { + DECL_AM(unknown); + BIF_RET(AM_unknown); + } + } + else if (ERTS_IS_ATOM_STR("cpu_quota", BIF_ARG_1)) { + int no; + erts_get_logical_processors(NULL, NULL, NULL, &no); if (no > 0) BIF_RET(make_small((Uint) no)); else { diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c index 6a4f43297e..67eebfe8f6 100644 --- a/erts/emulator/beam/erl_cpu_topology.c +++ b/erts/emulator/beam/erl_cpu_topology.c @@ -1632,7 +1632,7 @@ erts_get_cpu_topology_term(Process *c_p, Eterm which) } static void -get_logical_processors(int *conf, int *onln, int *avail) +get_logical_processors(int *conf, int *onln, int *avail, int *quota) { if (conf) *conf = erts_get_cpu_configured(cpuinfo); @@ -1640,13 +1640,15 @@ get_logical_processors(int *conf, int *onln, int *avail) *onln = erts_get_cpu_online(cpuinfo); if (avail) *avail = erts_get_cpu_available(cpuinfo); + if (quota) + *quota = erts_get_cpu_quota(cpuinfo); } void -erts_get_logical_processors(int *conf, int *onln, int *avail) +erts_get_logical_processors(int *conf, int *onln, int *avail, int *quota) { erts_rwmtx_rlock(&cpuinfo_rwmtx); - get_logical_processors(conf, onln, avail); + get_logical_processors(conf, onln, avail, quota); erts_rwmtx_runlock(&cpuinfo_rwmtx); } @@ -1655,14 +1657,15 @@ erts_pre_early_init_cpu_topology(int *max_dcg_p, int *max_rg_p, int *conf_p, int *onln_p, - int *avail_p) + int *avail_p, + int *quota_p) { cpu_groups_maps = NULL; no_cpu_groups_callbacks = 0; *max_rg_p = ERTS_MAX_READER_GROUPS; *max_dcg_p = ERTS_MAX_FLXCTR_GROUPS; cpuinfo = erts_cpu_info_create(); - get_logical_processors(conf_p, onln_p, avail_p); + get_logical_processors(conf_p, onln_p, avail_p, quota_p); } void diff --git a/erts/emulator/beam/erl_cpu_topology.h b/erts/emulator/beam/erl_cpu_topology.h index 4a428d7972..91e1322504 100644 --- a/erts/emulator/beam/erl_cpu_topology.h +++ b/erts/emulator/beam/erl_cpu_topology.h @@ -32,7 +32,8 @@ erts_pre_early_init_cpu_topology(int *max_dcg_p, int *max_rg_p, int *conf_p, int *onln_p, - int *avail_p); + int *avail_p, + int *quota_p); void erts_early_init_cpu_topology(int no_schedulers, int *max_main_threads_p, @@ -81,7 +82,7 @@ Eterm erts_set_cpu_topology(Process *c_p, Eterm term); Eterm erts_get_cpu_topology_term(Process *c_p, Eterm which); int erts_update_cpu_info(void); -void erts_get_logical_processors(int *conf, int *onln, int *avail); +void erts_get_logical_processors(int *conf, int *onln, int *avail, int *quota); int erts_sched_bind_atthrcreate_prepare(void); int erts_sched_bind_atthrcreate_child(int unbind); diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 547e4064a2..65cc66ebfc 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -774,6 +774,7 @@ early_init(int *argc, char **argv) /* int ncpu; int ncpuonln; int ncpuavail; + int ncpuquota; int schdlrs; int schdlrs_onln; int schdlrs_percentage = 100; @@ -811,7 +812,8 @@ early_init(int *argc, char **argv) /* &max_reader_groups, &ncpu, &ncpuonln, - &ncpuavail); + &ncpuavail, + &ncpuquota); ignore_break = 0; replace_intr = 0; @@ -838,9 +840,18 @@ early_init(int *argc, char **argv) /* * can initialize the allocators. */ no_schedulers = (Uint) (ncpu > 0 ? ncpu : 1); - no_schedulers_online = (ncpuavail > 0 - ? ncpuavail - : (ncpuonln > 0 ? ncpuonln : no_schedulers)); + + if (ncpuavail > 0) { + if (ncpuquota > 0) { + no_schedulers_online = MIN(ncpuquota, ncpuavail); + } else { + no_schedulers_online = ncpuavail; + } + } else if (ncpuonln > 0) { + no_schedulers_online = ncpuonln; + } else { + no_schedulers_online = no_schedulers; + } schdlrs = no_schedulers; schdlrs_onln = no_schedulers_online; diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index f61949c75b..04dfd6a49b 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -993,62 +993,81 @@ sct_cmd(Config) when is_list(Config) -> {"db", thread_no_node_processor_spread}]). sbt_cmd(Config) when is_list(Config) -> - Bind = try - OldVal = erlang:system_flag(scheduler_bind_type, default_bind), - erlang:system_flag(scheduler_bind_type, OldVal), - go_for_it - catch - error:notsup -> notsup; - error:_ -> go_for_it - end, - case Bind of - notsup -> - {skipped, "Binding of schedulers not supported"}; - go_for_it -> - CpuTCmd = case erlang:system_info({cpu_topology,detected}) of - undefined -> - case os:type() of - linux -> - case erlang:system_info(logical_processors) of - 1 -> - "+sctL0"; - N when is_integer(N) -> - NS = integer_to_list(N-1), - "+sctL0-"++NS++"p0-"++NS; - _ -> - false - end; - _ -> - false - end; - _ -> - "" - end, - case CpuTCmd of - false -> - {skipped, "Don't know how to create cpu topology"}; - _ -> - case erlang:system_info(logical_processors) of - LP when is_integer(LP) -> - OldRelFlags = clear_erl_rel_flags(), - try - lists:foreach(fun ({ClBt, Bt}) -> - sbt_test(Config, - CpuTCmd, - ClBt, - Bt, - LP) - end, - ?BIND_TYPES) - after - restore_erl_rel_flags(OldRelFlags) - end, - ok; - _ -> - {skipped, - "Don't know the amount of logical processors"} - end - end + case sbt_check_prereqs() of + {skipped, _Reason}=Skipped -> + Skipped; + ok -> + case sbt_make_topology_args() of + false -> + {skipped, "Don't know how to create cpu topology"}; + CpuTCmd -> + LP = erlang:system_info(logical_processors), + OldRelFlags = clear_erl_rel_flags(), + try + lists:foreach(fun ({ClBt, Bt}) -> + sbt_test(Config, CpuTCmd, + ClBt, Bt, LP) + end, + ?BIND_TYPES) + after + restore_erl_rel_flags(OldRelFlags) + end, + ok + end + end. + +sbt_make_topology_args() -> + case erlang:system_info({cpu_topology,detected}) of + undefined -> + case os:type() of + linux -> + case erlang:system_info(logical_processors) of + 1 -> + "+sctL0"; + N -> + NS = integer_to_list(N - 1), + "+sctL0-"++NS++"p0-"++NS + end; + _ -> + false + end; + _ -> + "" + end. + +sbt_check_prereqs() -> + try + Available = erlang:system_info(logical_processors_available), + Quota = erlang:system_info(cpu_quota), + if + Quota =:= unknown; Quota >= Available -> + ok; + Quota < Available -> + throw({skipped, "Test requires that CPU quota is greater than " + "the number of available processors."}) + end, + + try + OldVal = erlang:system_flag(scheduler_bind_type, default_bind), + erlang:system_flag(scheduler_bind_type, OldVal) + catch + error:notsup -> + throw({skipped, "Scheduler binding not supported."}); + error:_ -> + %% ?! + ok + end, + + case erlang:system_info(logical_processors) of + Count when is_integer(Count) -> + ok; + unknown -> + throw({skipped, "Can't detect number of logical processors."}) + end, + + ok + catch + throw:{skip,_Reason}=Skip -> Skip end. sbt_test(Config, CpuTCmd, ClBt, Bt, LP) -> @@ -1110,28 +1129,48 @@ scheduler_threads(Config) when is_list(Config) -> {Sched, HalfSchedOnln, _} = get_sstate(Config, "+SP:50"), %% Configure 2x scheduler threads only {TwiceSched, SchedOnln, _} = get_sstate(Config, "+SP 200"), - case {erlang:system_info(logical_processors), - erlang:system_info(logical_processors_available)} of - {LProc, LProcAvail} when is_integer(LProc), is_integer(LProcAvail) -> - %% Test resetting the scheduler counts - ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0", - {LProc, LProcAvail, _} = get_sstate(Config, ResetCmd), - %% Test negative +S settings, but only for SMP-enabled emulators - case {LProc > 1, LProcAvail > 1} of - {true, true} -> - SchedMinus1 = LProc-1, - SchedOnlnMinus1 = LProcAvail-1, - {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"), - {LProc, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"), - {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1"), - ok; - _ -> - {comment, "Skipped reduced amount of schedulers test due to too few logical processors"} - end; - _ -> %% Skipped when missing info about logical processors... - {comment, "Skipped reset amount of schedulers test, and reduced amount of schedulers test due to too unknown amount of logical processors"} + + LProc = erlang:system_info(logical_processors), + LProcAvail = erlang:system_info(logical_processors_available), + Quota = erlang:system_info(cpu_quota), + + if + not is_integer(LProc); not is_integer(LProcAvail) -> + {comment, "Skipped reset amount of schedulers test, and reduced " + "amount of schedulers test due to too unknown amount of " + "logical processors"}; + is_integer(LProc); is_integer(LProcAvail) -> + ExpectedOnln = st_expected_onln(LProcAvail, Quota), + + st_reset(Config, LProc, ExpectedOnln, FourSched, FourSchedOnln), + + if + LProc =:= 1; LProcAvail =:= 1 -> + {comment, "Skipped reduced amount of schedulers test due " + "to too few logical processors"}; + LProc > 1, LProcAvail > 1 -> + st_reduced(Config, LProc, ExpectedOnln) + end end. +st_reset(Config, LProc, ExpectedOnln, FourSched, FourSchedOnln) -> + %% Test resetting # of schedulers. + ResetCmd = "+S "++FourSched++":"++FourSchedOnln++" +S 0:0", + {LProc, ExpectedOnln, _} = get_sstate(Config, ResetCmd), + ok. + +st_reduced(Config, LProc, ExpectedOnln) -> + %% Test negative +S settings + SchedMinus1 = LProc-1, + SchedOnlnMinus1 = ExpectedOnln-1, + {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1"), + {LProc, SchedOnlnMinus1, _} = get_sstate(Config, "+S :-1"), + {SchedMinus1, SchedOnlnMinus1, _} = get_sstate(Config, "+S -1:-1"), + ok. + +st_expected_onln(LProcAvail, unknown) -> LProcAvail; +st_expected_onln(LProcAvail, Quota) -> min(LProcAvail, Quota). + dirty_scheduler_threads(Config) when is_list(Config) -> case erlang:system_info(dirty_cpu_schedulers) of 0 -> {skipped, "No dirty scheduler support"}; diff --git a/erts/include/internal/erl_misc_utils.h b/erts/include/internal/erl_misc_utils.h index 55566ddf74..59933ade4b 100644 --- a/erts/include/internal/erl_misc_utils.h +++ b/erts/include/internal/erl_misc_utils.h @@ -39,6 +39,7 @@ int erts_cpu_info_update(erts_cpu_info_t *cpuinfo); int erts_get_cpu_configured(erts_cpu_info_t *cpuinfo); int erts_get_cpu_online(erts_cpu_info_t *cpuinfo); int erts_get_cpu_available(erts_cpu_info_t *cpuinfo); +int erts_get_cpu_quota(erts_cpu_info_t *cpuinfo); char *erts_get_unbind_from_cpu_str(erts_cpu_info_t *cpuinfo); int erts_get_available_cpu(erts_cpu_info_t *cpuinfo, int no); int erts_get_cpu_topology_size(erts_cpu_info_t *cpuinfo); diff --git a/erts/lib_src/common/erl_misc_utils.c b/erts/lib_src/common/erl_misc_utils.c index 55fac13e95..6731137bdf 100644 --- a/erts/lib_src/common/erl_misc_utils.c +++ b/erts/lib_src/common/erl_misc_utils.c @@ -54,6 +54,7 @@ # endif # endif # include <string.h> +# include <stdio.h> # ifdef HAVE_UNISTD_H # include <unistd.h> # endif @@ -133,6 +134,7 @@ #endif static int read_topology(erts_cpu_info_t *cpuinfo); +static int read_cpu_quota(int limit); #if defined(ERTS_HAVE_MISC_UTIL_AFFINITY_MASK__) static int @@ -176,6 +178,7 @@ struct erts_cpu_info_t_ { int online; int available; int topology_size; + int quota; erts_cpu_topology_t *topology; #if defined(ERTS_HAVE_MISC_UTIL_AFFINITY_MASK__) char *affinity_str; @@ -238,6 +241,7 @@ erts_cpu_info_create(void) cpuinfo->configured = -1; cpuinfo->online = -1; cpuinfo->available = -1; + cpuinfo->quota = -1; erts_cpu_info_update(cpuinfo); return cpuinfo; } @@ -269,6 +273,7 @@ erts_cpu_info_update(erts_cpu_info_t *cpuinfo) int configured = 0; int online = 0; int available = 0; + int quota = 0; erts_cpu_topology_t *old_topology; int old_topology_size; #if defined(ERTS_HAVE_MISC_UTIL_AFFINITY_MASK__) @@ -412,9 +417,14 @@ erts_cpu_info_update(erts_cpu_info_t *cpuinfo) if (cpuinfo->available != available) changed = 1; + quota = read_cpu_quota(online); + if (cpuinfo->quota != quota) + changed = 1; + cpuinfo->configured = configured; cpuinfo->online = online; cpuinfo->available = available; + cpuinfo->quota = quota; old_topology = cpuinfo->topology; old_topology_size = cpuinfo->topology_size; @@ -471,6 +481,16 @@ erts_get_cpu_available(erts_cpu_info_t *cpuinfo) return cpuinfo->available; } +int +erts_get_cpu_quota(erts_cpu_info_t *cpuinfo) +{ + if (!cpuinfo) + return -EINVAL; + if (cpuinfo->quota <= 0) + return -ENOTSUP; + return cpuinfo->quota; +} + char * erts_get_unbind_from_cpu_str(erts_cpu_info_t *cpuinfo) { @@ -775,7 +795,7 @@ adjust_processor_nodes(erts_cpu_info_t *cpuinfo, int no_nodes) #ifdef __linux__ static int -read_file(char *path, char *buf, int size) +read_file(const char *path, char *buf, int size) { int ix = 0; ssize_t sz = size-1; @@ -999,6 +1019,211 @@ read_topology(erts_cpu_info_t *cpuinfo) return res; } +static int +csv_contains(const char *haystack, + const char *element, + char separator) { + size_t element_len; + const char *ptr; + + element_len = strlen(element); + ptr = strstr(haystack, element); + + while (ptr) { + if (!ptr[element_len] || ptr[element_len] == separator) { + if (ptr == haystack || ptr[-1] == separator) { + return 1; + } + } + + ptr = strstr(&ptr[1], element); + } + + return 0; +} + +static const char* +str_combine(const char *a, const char *b) { + size_t a_len, b_len; + char *result; + + a_len = strlen(a); + b_len = strlen(b); + + result = malloc(a_len + b_len + 1); + + memcpy(&result[0], a, a_len); + memcpy(&result[a_len], b, b_len + 1); + + return result; +} + +static const char* +get_cgroup_v1_base_dir(const char *controller) { + char line_buf[5 << 10]; + FILE *var_file; + + var_file = fopen("/proc/self/cgroup", "r"); + + if (var_file == NULL) { + return NULL; + } + + while (fgets(line_buf, sizeof(line_buf), var_file)) { + /* sscanf_s requires C11, so we use hardcoded sizes (rather than rely + * on macros like MAXPATHLEN) so we can specify them directly in the + * format string. */ + char base_dir[4 << 10]; + char controllers[256]; + + if (sscanf(line_buf, "%*d:%255[^:]:%4095s\n", + controllers, base_dir) != 2) { + continue; + } + + if (csv_contains(controllers, controller, ',')) { + fclose(var_file); + return strdup(base_dir); + } + } + + fclose(var_file); + return NULL; +} + +static const char* +get_cgroup_path(const char *controller) { + char line_buf[10 << 10]; + FILE *var_file; + + var_file = fopen("/proc/self/mountinfo", "r"); + + if (var_file == NULL) { + return NULL; + } + + while (fgets(line_buf, sizeof(line_buf), var_file)) { + char mount_path[4 << 10]; + char root_path[4 << 10]; + char fs_flags[512]; + char fs_type[64]; + + /* Format: + * [Mount id] [Parent id] [Major] [Minor] [Root] [Mounted at] \ + * [Mount flags] ... (options terminated by a single hyphen) ... \ + * [FS type] [Mount source] [Flags] + * + * (See proc(5) for a more complete description.) + * + * This fails if any of the fs options contain a hyphen, but this is + * not likely to happen on a cgroup, so we just skip such lines. */ + if (sscanf(line_buf, + "%*d %*d %*d:%*d %4095s %4095s %*s %*[^-]- " + "%63s %*s %511[^\n]\n", + root_path, mount_path, + fs_type, fs_flags) != 4) { + continue; + } + + if (!strcmp(fs_type, "cgroup2")) { + char controllers[256]; + const char *cgc_path; + + cgc_path = str_combine(mount_path, "/cgroup.controllers"); + if (read_file(cgc_path, controllers, sizeof(controllers)) > 0) { + if (csv_contains(controllers, controller, ' ')) { + free((void*)cgc_path); + fclose(var_file); + return strdup(mount_path); + } + } + free((void*)cgc_path); + } else if (!strcmp(fs_type, "cgroup")) { + if (csv_contains(fs_flags, controller, ',')) { + const char *base_dir = get_cgroup_v1_base_dir(controller); + + if (base_dir) { + const char *result; + + if (strcmp(root_path, base_dir)) { + result = str_combine(mount_path, base_dir); + } else { + result = strdup(mount_path); + } + + free((void*)base_dir); + fclose(var_file); + return result; + } + } + } + } + + fclose(var_file); + return NULL; +} + +static int read_cgroup_var(const char *group_path, const char *var_name, + ssize_t *out) { + const char *var_path; + int res; + + var_path = str_combine(group_path, var_name); + res = 0; + + if (var_path) { + FILE *var_file = fopen(var_path, "r"); + free((void*)var_path); + + if (var_file) { + if (fscanf(var_file, "%zi", out) == 1) { + res = 1; + } + fclose(var_file); + } + } + + return res; +} + +/* CPU quotas are read from the cgroup configuration, which can be pretty hairy + * as we need to support both v1 and v2, and it's possible for both versions to + * be active at the same time. */ + +static int +read_cpu_quota(int limit) +{ + const char *cgroup_path = get_cgroup_path("cpu"); + + if (cgroup_path) { + ssize_t cfs_period_us, cfs_quota_us; + int succeeded; + + cfs_period_us = -1; + cfs_quota_us = -1; + + succeeded = + read_cgroup_var(cgroup_path, "/cpu.cfs_quota_us", &cfs_quota_us) && + read_cgroup_var(cgroup_path, "/cpu.cfs_period_us", &cfs_period_us); + + free((void*)cgroup_path); + + if (succeeded) { + if (cfs_period_us > 0 && cfs_quota_us > 0) { + size_t quota = cfs_quota_us / cfs_period_us; + + if (quota > 0 && quota <= (size_t)limit) { + return quota; + } + } + + return limit; + } + } + + return 0; +} + #elif defined(HAVE_KSTAT) /* SunOS kstat */ #include <kstat.h> @@ -1152,6 +1377,13 @@ read_topology(erts_cpu_info_t *cpuinfo) } +static int +read_cpu_quota(int limit) +{ + (void)limit; + return 0; +} + #elif defined(__WIN32__) /* @@ -1426,6 +1658,13 @@ read_topology(erts_cpu_info_t *cpuinfo) return res; } +static int +read_cpu_quota(int limit) +{ + (void)limit; + return 0; +} + #elif defined(__FreeBSD__) /** @@ -1665,9 +1904,23 @@ error: return res; } +static int +read_cpu_quota(int limit) +{ + (void)limit; + return 0; +} + #else static int +read_cpu_quota(int limit) +{ + (void)limit; + return 0; +} + +static int read_topology(erts_cpu_info_t *cpuinfo) { return -ENOTSUP; |