summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--erts/doc/src/erl.xml21
-rw-r--r--erts/doc/src/erlang.xml14
-rw-r--r--erts/emulator/beam/erl_bif_info.c16
-rw-r--r--erts/emulator/beam/erl_cpu_topology.c13
-rw-r--r--erts/emulator/beam/erl_cpu_topology.h5
-rw-r--r--erts/emulator/beam/erl_init.c19
-rw-r--r--erts/emulator/test/scheduler_SUITE.erl191
-rw-r--r--erts/include/internal/erl_misc_utils.h1
-rw-r--r--erts/lib_src/common/erl_misc_utils.c255
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;