diff options
Diffstat (limited to 'src/gc/unix')
-rw-r--r-- | src/gc/unix/cgroup.cpp | 203 | ||||
-rw-r--r-- | src/gc/unix/configure.cmake | 2 | ||||
-rw-r--r-- | src/gc/unix/events.cpp | 4 | ||||
-rw-r--r-- | src/gc/unix/gcenv.unix.cpp | 24 |
4 files changed, 171 insertions, 62 deletions
diff --git a/src/gc/unix/cgroup.cpp b/src/gc/unix/cgroup.cpp index 1775ef7ff0..992678b634 100644 --- a/src/gc/unix/cgroup.cpp +++ b/src/gc/unix/cgroup.cpp @@ -9,7 +9,7 @@ Module Name: cgroup.cpp Abstract: - Read memory limits for the current process + Read memory and cpu limits for the current process --*/ #include <cstdint> #include <cstddef> @@ -26,42 +26,24 @@ Abstract: #define PROC_CGROUP_FILENAME "/proc/self/cgroup" #define PROC_STATM_FILENAME "/proc/self/statm" #define MEM_LIMIT_FILENAME "/memory.limit_in_bytes" +#define CFS_QUOTA_FILENAME "/cpu.cfs_quota_us" +#define CFS_PERIOD_FILENAME "/cpu.cfs_period_us" class CGroup { char* m_memory_cgroup_path; + char* m_cpu_cgroup_path; public: CGroup() { - m_memory_cgroup_path = nullptr; - char* memoryHierarchyMount = nullptr; - char *cgroup_path_relative_to_mount = nullptr; - size_t len; - memoryHierarchyMount = FindMemoryHierarchyMount(); - if (memoryHierarchyMount == nullptr) - goto done; - - cgroup_path_relative_to_mount = FindCGroupPathForMemorySubsystem(); - if (cgroup_path_relative_to_mount == nullptr) - goto done; - - len = strlen(memoryHierarchyMount); - len += strlen(cgroup_path_relative_to_mount); - m_memory_cgroup_path = (char*)malloc(len+1); - if (m_memory_cgroup_path == nullptr) - goto done; - - strcpy(m_memory_cgroup_path, memoryHierarchyMount); - strcat(m_memory_cgroup_path, cgroup_path_relative_to_mount); - - done: - free(memoryHierarchyMount); - free(cgroup_path_relative_to_mount); + m_memory_cgroup_path = FindCgroupPath(&IsMemorySubsystem); + m_cpu_cgroup_path = FindCgroupPath(&IsCpuSubsystem); } ~CGroup() { free(m_memory_cgroup_path); + free(m_cpu_cgroup_path); } bool GetPhysicalMemoryLimit(size_t *val) @@ -84,15 +66,89 @@ public: free(mem_limit_filename); return result; } + + bool GetCpuLimit(uint32_t *val) + { + long long quota; + long long period; + long long cpu_count; + + quota = ReadCpuCGroupValue(CFS_QUOTA_FILENAME); + if (quota <= 0) + return false; + + period = ReadCpuCGroupValue(CFS_PERIOD_FILENAME); + if (period <= 0) + return false; + + // Cannot have less than 1 CPU + if (quota <= period) + { + *val = 1; + return true; + } + + cpu_count = quota / period; + if (cpu_count < UINT32_MAX) + { + *val = cpu_count; + } + else + { + *val = UINT32_MAX; + } + + return true; + } private: - char* FindMemoryHierarchyMount() + static bool IsMemorySubsystem(const char *strTok){ + return strcmp("memory", strTok) == 0; + } + + static bool IsCpuSubsystem(const char *strTok){ + return strcmp("cpu", strTok) == 0; + } + + static char* FindCgroupPath(bool (*is_subsystem)(const char *)){ + char *cgroup_path = nullptr; + char *hierarchy_mount = nullptr; + char *hierarchy_root = nullptr; + char *cgroup_path_relative_to_mount = nullptr; + + FindHierarchyMount(is_subsystem, &hierarchy_mount, &hierarchy_root); + if (hierarchy_mount == nullptr || hierarchy_root == nullptr) + goto done; + + cgroup_path_relative_to_mount = FindCGroupPathForSubsystem(is_subsystem); + if (cgroup_path_relative_to_mount == nullptr) + goto done; + + cgroup_path = (char*)malloc(strlen(hierarchy_mount) + strlen(cgroup_path_relative_to_mount) + 1); + if (cgroup_path == nullptr) + goto done; + + strcpy(cgroup_path, hierarchy_mount); + // For a host cgroup, we need to append the relative path. + // In a docker container, the root and relative path are the same and we don't need to append. + if (strcmp(hierarchy_root, cgroup_path_relative_to_mount) != 0) + strcat(cgroup_path, cgroup_path_relative_to_mount); + + done: + free(hierarchy_mount); + free(hierarchy_root); + free(cgroup_path_relative_to_mount); + return cgroup_path; + } + + static void FindHierarchyMount(bool (*is_subsystem)(const char *), char** pmountpath, char** pmountroot) { char *line = nullptr; size_t lineLen = 0, maxLineLen = 0; char *filesystemType = nullptr; char *options = nullptr; - char* mountpath = nullptr; + char *mountpath = nullptr; + char *mountroot = nullptr; FILE *mountinfofile = fopen(PROC_MOUNTINFO_FILENAME, "r"); if (mountinfofile == nullptr) @@ -113,11 +169,11 @@ private: maxLineLen = lineLen; } - char* separatorChar = strchr(line, '-'); + char* separatorChar = strstr(line, " - "); // See man page of proc to get format for /proc/self/mountinfo file int sscanfRet = sscanf(separatorChar, - "- %s %*s %s", + " - %s %*s %s", filesystemType, options); if (sscanfRet != 2) @@ -132,21 +188,26 @@ private: char* strTok = strtok_r(options, ",", &context); while (strTok != nullptr) { - if (strncmp("memory", strTok, 6) == 0) + if (is_subsystem(strTok)) { mountpath = (char*)malloc(lineLen+1); if (mountpath == nullptr) goto done; + mountroot = (char*)malloc(lineLen+1); + if (mountroot == nullptr) + goto done; sscanfRet = sscanf(line, - "%*s %*s %*s %*s %s ", + "%*s %*s %*s %s %s ", + mountroot, mountpath); - if (sscanfRet != 1) - { - free(mountpath); - mountpath = nullptr; + if (sscanfRet != 2) assert(!"Failed to parse mount info file contents with sscanf."); - } + + // assign the output arguments and clear the locals so we don't free them. + *pmountpath = mountpath; + *pmountroot = mountroot; + mountpath = mountroot = nullptr; goto done; } strTok = strtok_r(nullptr, ",", &context); @@ -154,15 +215,16 @@ private: } } done: + free(mountpath); + free(mountroot); free(filesystemType); free(options); free(line); if (mountinfofile) fclose(mountinfofile); - return mountpath; } - char* FindCGroupPathForMemorySubsystem() + static char* FindCGroupPathForSubsystem(bool (*is_subsystem)(const char *)) { char *line = nullptr; size_t lineLen = 0; @@ -205,7 +267,7 @@ private: char* strTok = strtok_r(subsystem_list, ",", &context); while (strTok != nullptr) { - if (strncmp("memory", strTok, 6) == 0) + if (is_subsystem(strTok)) { result = true; break; @@ -271,6 +333,59 @@ private: free(line); return result; } + + long long ReadCpuCGroupValue(const char* subsystemFilename){ + char *filename = nullptr; + bool result = false; + long long val; + + if (m_cpu_cgroup_path == nullptr) + return -1; + + filename = (char*)malloc(strlen(m_cpu_cgroup_path) + strlen(subsystemFilename) + 1); + if (filename == nullptr) + return -1; + + strcpy(filename, m_cpu_cgroup_path); + strcat(filename, subsystemFilename); + result = ReadLongLongValueFromFile(filename, &val); + free(filename); + if (!result) + return -1; + + return val; + } + + bool ReadLongLongValueFromFile(const char* filename, long long* val) + { + bool result = false; + char *line = nullptr; + size_t lineLen = 0; + + FILE* file = nullptr; + + if (val == nullptr) + goto done; + + file = fopen(filename, "r"); + if (file == nullptr) + goto done; + + if (getline(&line, &lineLen, file) == -1) + goto done; + + errno = 0; + *val = atoll(line); + if (errno != 0) + goto done; + + result = true; + done: + if (file) + fclose(file); + free(line); + return result; + } }; size_t GetRestrictedPhysicalMemoryLimit() @@ -340,3 +455,13 @@ bool GetWorkingSetSize(size_t* val) free(line); return result; } + +bool GetCpuLimit(uint32_t* val) +{ + CGroup cgroup; + + if (val == nullptr) + return false; + + return cgroup.GetCpuLimit(val); +} diff --git a/src/gc/unix/configure.cmake b/src/gc/unix/configure.cmake index 74ae70b1a4..b118232b35 100644 --- a/src/gc/unix/configure.cmake +++ b/src/gc/unix/configure.cmake @@ -54,4 +54,4 @@ check_cxx_source_runs(" check_library_exists(c sched_getaffinity "" HAVE_SCHED_GETAFFINITY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) +configure_file(${CMAKE_CURRENT_LIST_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) diff --git a/src/gc/unix/events.cpp b/src/gc/unix/events.cpp index 7c665f4aaa..694b9ba17c 100644 --- a/src/gc/unix/events.cpp +++ b/src/gc/unix/events.cpp @@ -11,10 +11,6 @@ #include <errno.h> #include "config.h" -#ifndef __out_z -#define __out_z -#endif // __out_z - #include "gcenv.structs.h" #include "gcenv.base.h" #include "gcenv.os.h" diff --git a/src/gc/unix/gcenv.unix.cpp b/src/gc/unix/gcenv.unix.cpp index 5693fb4ff5..f564b28239 100644 --- a/src/gc/unix/gcenv.unix.cpp +++ b/src/gc/unix/gcenv.unix.cpp @@ -6,32 +6,15 @@ #include <cstddef> #include <cassert> #include <memory> - -// The CoreCLR PAL defines _POSIX_C_SOURCE to avoid calling non-posix pthread functions. -// This isn't something we want, because we're totally fine using non-posix functions. -#if defined(__APPLE__) - #define _DARWIN_C_SOURCE -#endif // definfed(__APPLE__) - #include <pthread.h> #include <signal.h> #include "config.h" -// clang typedefs uint64_t to be unsigned long long, which clashes with -// PAL/MSVC's unsigned long, causing linker errors. This ugly hack -// will go away once the GC doesn't depend on PAL headers. -typedef unsigned long uint64_t_hack; -#define uint64_t uint64_t_hack -static_assert(sizeof(uint64_t) == 8, "unsigned long isn't 8 bytes"); - -#ifndef __out_z -#define __out_z -#endif // __out_z - #include "gcenv.structs.h" #include "gcenv.base.h" #include "gcenv.os.h" #include "gcenv.unix.inl" +#include "volatile.h" #if HAVE_SYS_TIME_H #include <sys/time.h> @@ -66,6 +49,7 @@ static pthread_mutex_t g_flushProcessWriteBuffersMutex; size_t GetRestrictedPhysicalMemoryLimit(); bool GetWorkingSetSize(size_t* val); +bool GetCpuLimit(uint32_t* val); static size_t g_RestrictedPhysicalMemoryLimit = 0; @@ -507,6 +491,7 @@ bool GCToOSInterface::GetCurrentProcessAffinityMask(uintptr_t* processAffinityMa uint32_t GCToOSInterface::GetCurrentProcessCpuCount() { uintptr_t pmask, smask; + uint32_t cpuLimit; if (!GetCurrentProcessAffinityMask(&pmask, &smask)) return 1; @@ -530,6 +515,9 @@ uint32_t GCToOSInterface::GetCurrentProcessCpuCount() if (count == 0 || count > 64) count = 64; + if (GetCpuLimit(&cpuLimit) && cpuLimit < count) + count = cpuLimit; + return count; } |