Skip to content

Commit

Permalink
[mono] Determine any memory/CPU limitations from sysfs cgroup (#74237)
Browse files Browse the repository at this point in the history
Add capability to interrogate cgroup limitations when determining CP and memory limits

This code has been adapted from coreCLR. It has been modified from C++ but uses the same naming conventions in the event of a unified mechanism that can be shared between both runtimes being developed. The code has been tested on Ubuntu 20.04 and CentOS 7 with cgroupv1 and cgroupv2.

This code is required in the event of running runtime in a container as the current limitations being discovered by the mono runtime are purely for the machine and not in a container which may have lower quotas.
  • Loading branch information
nealef authored Aug 31, 2022
1 parent b82f254 commit 5119157
Show file tree
Hide file tree
Showing 11 changed files with 1,085 additions and 14 deletions.
1 change: 1 addition & 0 deletions src/mono/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(HOST_LINUX 1)
add_definitions(-D_GNU_SOURCE -D_REENTRANT)
add_definitions(-D_THREAD_SAFE)
set(HAVE_CGROUP_SUPPORT 1)
# Enable the "full RELRO" options (RELRO & BIND_NOW) at link time
add_link_options(-Wl,-z,relro)
add_link_options(-Wl,-z,now)
Expand Down
3 changes: 3 additions & 0 deletions src/mono/cmake/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,9 @@
/* Define to 1 if you have /usr/include/malloc.h. */
#cmakedefine HAVE_USR_INCLUDE_MALLOC_H 1

/* Define to 1 if you have linux cgroups */
#cmakedefine HAVE_CGROUP_SUPPORT 1

/* The architecture this is running on */
#define MONO_ARCHITECTURE @MONO_ARCHITECTURE@

Expand Down
2 changes: 1 addition & 1 deletion src/mono/mono/metadata/icall.c
Original file line number Diff line number Diff line change
Expand Up @@ -7189,7 +7189,7 @@ ves_icall_System_Threading_Thread_YieldInternal (void)
gint32
ves_icall_System_Environment_get_ProcessorCount (void)
{
return mono_cpu_count ();
return mono_cpu_limit ();
}

// Generate wrappers.
Expand Down
4 changes: 2 additions & 2 deletions src/mono/mono/sgen/sgen-marksweep.c
Original file line number Diff line number Diff line change
Expand Up @@ -2861,7 +2861,7 @@ sgen_marksweep_init_internal (SgenMajorCollector *collector, gboolean is_concurr

sgen_register_fixed_internal_mem_type (INTERNAL_MEM_MS_BLOCK_INFO, SIZEOF_MS_BLOCK_INFO);

if (mono_cpu_count () <= 1)
if (mono_cpu_limit () <= 1)
is_parallel = FALSE;

num_block_obj_sizes = ms_calculate_block_obj_sizes (MS_BLOCK_OBJ_SIZE_FACTOR, NULL);
Expand Down Expand Up @@ -3027,7 +3027,7 @@ sgen_marksweep_init_internal (SgenMajorCollector *collector, gboolean is_concurr

#ifndef DISABLE_SGEN_MAJOR_MARKSWEEP_CONC
if (is_concurrent && is_parallel)
sgen_workers_create_context (GENERATION_OLD, mono_cpu_count ());
sgen_workers_create_context (GENERATION_OLD, mono_cpu_limit ());
else if (is_concurrent)
sgen_workers_create_context (GENERATION_OLD, 1);

Expand Down
2 changes: 1 addition & 1 deletion src/mono/mono/sgen/sgen-simple-nursery.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ fill_parallel_with_concurrent_major_ops (SgenObjectOperations *ops)
void
sgen_simple_nursery_init (SgenMinorCollector *collector, gboolean parallel)
{
if (mono_cpu_count () <= 1)
if (mono_cpu_limit () <= 1)
parallel = FALSE;

#ifdef DISABLE_SGEN_MAJOR_MARKSWEEP_CONC
Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ set(utils_common_sources
mono-sha1.c
mono-logger.c
mono-logger-internals.h
mono-cgroup.c
mono-codeman.c
mono-counters.c
mono-compiler.h
Expand Down
60 changes: 50 additions & 10 deletions src/mono/mono/utils/memfuncs.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <config.h>
#include <glib.h>
#include <string.h>
#include <errno.h>

#if defined (__APPLE__)
#include <mach/message.h>
Expand Down Expand Up @@ -69,6 +70,7 @@
__d [__i] = NULL; \
} while (0)

#define MINMEMSZ 209715200 /* Minimum restricted memory size */

/**
* mono_gc_bzero_aligned:
Expand Down Expand Up @@ -273,24 +275,59 @@ mono_determine_physical_ram_size (void)

return (guint64)value;
#elif defined (HAVE_SYSCONF)
gint64 page_size = -1, num_pages = -1;
guint64 page_size = 0, num_pages = 0, memsize;

/* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
* reports invalid values, please add your OS specific code below. */
#ifdef _SC_PAGESIZE
page_size = (gint64)sysconf (_SC_PAGESIZE);
page_size = (guint64)sysconf (_SC_PAGESIZE);
#endif

#ifdef _SC_PHYS_PAGES
num_pages = (gint64)sysconf (_SC_PHYS_PAGES);
num_pages = (guint64)sysconf (_SC_PHYS_PAGES);
#endif

if (page_size == -1 || num_pages == -1) {
if (!page_size || !num_pages) {
g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
return _DEFAULT_MEM_SIZE;
}

return (guint64)page_size * (guint64)num_pages;
#if defined(_SC_AVPHYS_PAGES)
memsize = sysconf(_SC_AVPHYS_PAGES) * page_size;
#else
memsize = page_size * num_pages; /* Calculate physical memory size */
#endif

#if HAVE_CGROUP_SUPPORT
gint64 restricted_limit = mono_get_restricted_memory_limit(); /* Check for any cgroup limit */
if (restricted_limit != 0) {
gchar *heapHardLimit = getenv("DOTNET_GCHeapHardLimit"); /* See if user has set a limit */
if (heapHardLimit == NULL)
heapHardLimit = getenv("COMPlus_GCHeapHardLimit"); /* Check old envvar name */
errno = 0;
if (heapHardLimit != NULL) {
guint64 gcLimit = strtoull(heapHardLimit, NULL, 16);
if ((errno == 0) && (gcLimit != 0))
restricted_limit = (restricted_limit < gcLimit ? restricted_limit : (gint64) gcLimit);
} else {
gchar *heapHardLimitPct = getenv("DOTNET_GCHeapHardLimitPercent"); /* User % limit? */
if (heapHardLimitPct == NULL)
heapHardLimitPct = getenv("COMPlus_GCHeapHardLimitPercent"); /* Check old envvar name */
if (heapHardLimitPct != NULL) {
int gcLimit = strtoll(heapHardLimitPct, NULL, 16);
if ((gcLimit > 0) && (gcLimit <= 100))
restricted_limit = (gcLimit * restricted_limit) / 100;
else
restricted_limit = (3 * restricted_limit) / 4; /* Use 75% limit of container */
} else {
restricted_limit = (3 * restricted_limit) / 4; /* Use 75% limit of container */
}
}
return (restricted_limit < MINMEMSZ ? MINMEMSZ : /* Use at least 20MB */
(restricted_limit < memsize ? restricted_limit : memsize));
}
#endif
return memsize;
#else
return _DEFAULT_MEM_SIZE;
#endif
Expand Down Expand Up @@ -343,25 +380,28 @@ mono_determine_physical_ram_available_size (void)
host_page_size (host, &page_size);
return (guint64) vmstat.free_count * page_size;

#elif HAVE_CGROUP_SUPPORT
return (mono_get_memory_avail());

#elif defined (HAVE_SYSCONF)
gint64 page_size = -1, num_pages = -1;
guint64 page_size = 0, num_pages = 0;

/* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
* reports invalid values, please add your OS specific code below. */
#ifdef _SC_PAGESIZE
page_size = (gint64)sysconf (_SC_PAGESIZE);
page_size = (guint64)sysconf (_SC_PAGESIZE);
#endif

#ifdef _SC_AVPHYS_PAGES
num_pages = (gint64)sysconf (_SC_AVPHYS_PAGES);
num_pages = (guint64)sysconf (_SC_AVPHYS_PAGES);
#endif

if (page_size == -1 || num_pages == -1) {
if (!page_size || !num_pages) {
g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
return _DEFAULT_MEM_SIZE;
}

return (guint64)page_size * (guint64)num_pages;
return page_size * num_pages;
#else
return _DEFAULT_MEM_SIZE;
#endif
Expand Down
5 changes: 5 additions & 0 deletions src/mono/mono/utils/memfuncs.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,10 @@ MONO_COMPONENT_API void mono_gc_memmove_atomic (void *dest, const void *src, siz
void mono_gc_memmove_aligned (void *dest, const void *src, size_t size);
guint64 mono_determine_physical_ram_size (void);
guint64 mono_determine_physical_ram_available_size (void);
#if HAVE_CGROUP_SUPPORT
size_t mono_get_restricted_memory_limit(void);
gboolean mono_get_memory_used(size_t *);
size_t mono_get_memory_avail(void);
#endif

#endif
Loading

0 comments on commit 5119157

Please sign in to comment.