Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
jdk11u/src/hotspot/os/linux/osContainer_linux.cpp
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
709 lines (637 sloc)
23.3 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. | |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
* | |
* This code is free software; you can redistribute it and/or modify it | |
* under the terms of the GNU General Public License version 2 only, as | |
* published by the Free Software Foundation. | |
* | |
* This code is distributed in the hope that it will be useful, but WITHOUT | |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
* version 2 for more details (a copy is included in the LICENSE file that | |
* accompanied this code). | |
* | |
* You should have received a copy of the GNU General Public License version | |
* 2 along with this work; if not, write to the Free Software Foundation, | |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
* | |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
* or visit www.oracle.com if you need additional information or have any | |
* questions. | |
* | |
*/ | |
#include <string.h> | |
#include <math.h> | |
#include <errno.h> | |
#include "utilities/globalDefinitions.hpp" | |
#include "memory/allocation.hpp" | |
#include "runtime/os.hpp" | |
#include "logging/log.hpp" | |
#include "osContainer_linux.hpp" | |
#define PER_CPU_SHARES 1024 | |
bool OSContainer::_is_initialized = false; | |
bool OSContainer::_is_containerized = false; | |
int OSContainer::_active_processor_count = 1; | |
julong _unlimited_memory; | |
class CgroupSubsystem: CHeapObj<mtInternal> { | |
friend class OSContainer; | |
private: | |
volatile jlong _next_check_counter; | |
/* mountinfo contents */ | |
char *_root; | |
char *_mount_point; | |
/* Constructed subsystem directory */ | |
char *_path; | |
public: | |
CgroupSubsystem(char *root, char *mountpoint) { | |
_root = os::strdup(root); | |
_mount_point = os::strdup(mountpoint); | |
_path = NULL; | |
_next_check_counter = min_jlong; | |
} | |
/* | |
* Set directory to subsystem specific files based | |
* on the contents of the mountinfo and cgroup files. | |
*/ | |
void set_subsystem_path(char *cgroup_path) { | |
char buf[MAXPATHLEN+1]; | |
if (_root != NULL && cgroup_path != NULL) { | |
if (strcmp(_root, "/") == 0) { | |
int buflen; | |
strncpy(buf, _mount_point, MAXPATHLEN); | |
buf[MAXPATHLEN-1] = '\0'; | |
if (strcmp(cgroup_path,"/") != 0) { | |
buflen = strlen(buf); | |
if ((buflen + strlen(cgroup_path)) > (MAXPATHLEN-1)) { | |
return; | |
} | |
strncat(buf, cgroup_path, MAXPATHLEN-buflen); | |
buf[MAXPATHLEN-1] = '\0'; | |
} | |
_path = os::strdup(buf); | |
} else { | |
if (strcmp(_root, cgroup_path) == 0) { | |
strncpy(buf, _mount_point, MAXPATHLEN); | |
buf[MAXPATHLEN-1] = '\0'; | |
_path = os::strdup(buf); | |
} else { | |
char *p = strstr(cgroup_path, _root); | |
if (p != NULL && p == _root) { | |
if (strlen(cgroup_path) > strlen(_root)) { | |
int buflen; | |
strncpy(buf, _mount_point, MAXPATHLEN); | |
buf[MAXPATHLEN-1] = '\0'; | |
buflen = strlen(buf); | |
if ((buflen + strlen(cgroup_path) - strlen(_root)) > (MAXPATHLEN-1)) { | |
return; | |
} | |
strncat(buf, cgroup_path + strlen(_root), MAXPATHLEN-buflen); | |
buf[MAXPATHLEN-1] = '\0'; | |
_path = os::strdup(buf); | |
} | |
} | |
} | |
} | |
} | |
} | |
char *subsystem_path() { return _path; } | |
bool cache_has_expired() { | |
return os::elapsed_counter() > _next_check_counter; | |
} | |
void set_cache_expiry_time(jlong timeout) { | |
_next_check_counter = os::elapsed_counter() + timeout; | |
} | |
}; | |
class CgroupMemorySubsystem: CgroupSubsystem { | |
friend class OSContainer; | |
private: | |
/* Some container runtimes set limits via cgroup | |
* hierarchy. If set to true consider also memory.stat | |
* file if everything else seems unlimited */ | |
bool _uses_mem_hierarchy; | |
volatile jlong _memory_limit_in_bytes; | |
public: | |
CgroupMemorySubsystem(char *root, char *mountpoint) : CgroupSubsystem::CgroupSubsystem(root, mountpoint) { | |
_uses_mem_hierarchy = false; | |
_memory_limit_in_bytes = -1; | |
} | |
bool is_hierarchical() { return _uses_mem_hierarchy; } | |
void set_hierarchical(bool value) { _uses_mem_hierarchy = value; } | |
jlong memory_limit_in_bytes() { return _memory_limit_in_bytes; } | |
void set_memory_limit_in_bytes(jlong value) { | |
_memory_limit_in_bytes = value; | |
// max memory limit is unlikely to change, but we want to remain | |
// responsive to configuration changes. A very short grace time | |
// between re-read avoids excessive overhead during startup without | |
// significantly reducing the VMs ability to promptly react to reduced | |
// memory availability | |
set_cache_expiry_time(OSCONTAINER_CACHE_TIMEOUT); | |
} | |
}; | |
CgroupMemorySubsystem* memory = NULL; | |
CgroupSubsystem* cpuset = NULL; | |
CgroupSubsystem* cpu = NULL; | |
CgroupSubsystem* cpuacct = NULL; | |
typedef char * cptr; | |
PRAGMA_DIAG_PUSH | |
PRAGMA_FORMAT_NONLITERAL_IGNORED | |
template <typename T> int subsystem_file_line_contents(CgroupSubsystem* c, | |
const char *filename, | |
const char *matchline, | |
const char *scan_fmt, | |
T returnval) { | |
FILE *fp = NULL; | |
char *p; | |
char file[MAXPATHLEN+1]; | |
char buf[MAXPATHLEN+1]; | |
char discard[MAXPATHLEN+1]; | |
bool found_match = false; | |
if (c == NULL) { | |
log_debug(os, container)("subsystem_file_line_contents: CgroupSubsytem* is NULL"); | |
return OSCONTAINER_ERROR; | |
} | |
if (c->subsystem_path() == NULL) { | |
log_debug(os, container)("subsystem_file_line_contents: subsystem path is NULL"); | |
return OSCONTAINER_ERROR; | |
} | |
strncpy(file, c->subsystem_path(), MAXPATHLEN); | |
file[MAXPATHLEN-1] = '\0'; | |
int filelen = strlen(file); | |
if ((filelen + strlen(filename)) > (MAXPATHLEN-1)) { | |
log_debug(os, container)("File path too long %s, %s", file, filename); | |
return OSCONTAINER_ERROR; | |
} | |
strncat(file, filename, MAXPATHLEN-filelen); | |
log_trace(os, container)("Path to %s is %s", filename, file); | |
fp = fopen(file, "r"); | |
if (fp != NULL) { | |
int err = 0; | |
while ((p = fgets(buf, MAXPATHLEN, fp)) != NULL) { | |
found_match = false; | |
if (matchline == NULL) { | |
// single-line file case | |
int matched = sscanf(p, scan_fmt, returnval); | |
found_match = (matched == 1); | |
} else { | |
// multi-line file case | |
if (strstr(p, matchline) != NULL) { | |
// discard matchline string prefix | |
int matched = sscanf(p, scan_fmt, discard, returnval); | |
found_match = (matched == 2); | |
} else { | |
continue; // substring not found | |
} | |
} | |
if (found_match) { | |
fclose(fp); | |
return 0; | |
} else { | |
err = 1; | |
log_debug(os, container)("Type %s not found in file %s", scan_fmt, file); | |
} | |
} | |
if (err == 0) { | |
log_debug(os, container)("Empty file %s", file); | |
} | |
} else { | |
log_debug(os, container)("Open of file %s failed, %s", file, os::strerror(errno)); | |
} | |
if (fp != NULL) | |
fclose(fp); | |
return OSCONTAINER_ERROR; | |
} | |
PRAGMA_DIAG_POP | |
#define GET_CONTAINER_INFO(return_type, subsystem, filename, \ | |
logstring, scan_fmt, variable) \ | |
return_type variable; \ | |
{ \ | |
int err; \ | |
err = subsystem_file_line_contents(subsystem, \ | |
filename, \ | |
NULL, \ | |
scan_fmt, \ | |
&variable); \ | |
if (err != 0) \ | |
return (return_type) OSCONTAINER_ERROR; \ | |
\ | |
log_trace(os, container)(logstring, variable); \ | |
} | |
#define GET_CONTAINER_INFO_CPTR(return_type, subsystem, filename, \ | |
logstring, scan_fmt, variable, bufsize) \ | |
char variable[bufsize]; \ | |
{ \ | |
int err; \ | |
err = subsystem_file_line_contents(subsystem, \ | |
filename, \ | |
NULL, \ | |
scan_fmt, \ | |
variable); \ | |
if (err != 0) \ | |
return (return_type) NULL; \ | |
\ | |
log_trace(os, container)(logstring, variable); \ | |
} | |
#define GET_CONTAINER_INFO_LINE(return_type, subsystem, filename, \ | |
matchline, logstring, scan_fmt, variable) \ | |
return_type variable; \ | |
{ \ | |
int err; \ | |
err = subsystem_file_line_contents(subsystem, \ | |
filename, \ | |
matchline, \ | |
scan_fmt, \ | |
&variable); \ | |
if (err != 0) \ | |
return (return_type) OSCONTAINER_ERROR; \ | |
\ | |
log_trace(os, container)(logstring, variable); \ | |
} | |
/* init | |
* | |
* Initialize the container support and determine if | |
* we are running under cgroup control. | |
*/ | |
void OSContainer::init() { | |
FILE *mntinfo = NULL; | |
FILE *cgroup = NULL; | |
char buf[MAXPATHLEN+1]; | |
char tmproot[MAXPATHLEN+1]; | |
char tmpmount[MAXPATHLEN+1]; | |
char *p; | |
jlong mem_limit; | |
assert(!_is_initialized, "Initializing OSContainer more than once"); | |
_is_initialized = true; | |
_is_containerized = false; | |
_unlimited_memory = (LONG_MAX / os::vm_page_size()) * os::vm_page_size(); | |
log_trace(os, container)("OSContainer::init: Initializing Container Support"); | |
if (!UseContainerSupport) { | |
log_trace(os, container)("Container Support not enabled"); | |
return; | |
} | |
/* | |
* Find the cgroup mount point for memory and cpuset | |
* by reading /proc/self/mountinfo | |
* | |
* Example for docker: | |
* 219 214 0:29 /docker/7208cebd00fa5f2e342b1094f7bed87fa25661471a4637118e65f1c995be8a34 /sys/fs/cgroup/memory ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory | |
* | |
* Example for host: | |
* 34 28 0:29 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,memory | |
*/ | |
mntinfo = fopen("/proc/self/mountinfo", "r"); | |
if (mntinfo == NULL) { | |
log_debug(os, container)("Can't open /proc/self/mountinfo, %s", | |
os::strerror(errno)); | |
return; | |
} | |
while ((p = fgets(buf, MAXPATHLEN, mntinfo)) != NULL) { | |
char tmpcgroups[MAXPATHLEN+1]; | |
char *cptr = tmpcgroups; | |
char *token; | |
// mountinfo format is documented at https://www.kernel.org/doc/Documentation/filesystems/proc.txt | |
if (sscanf(p, "%*d %*d %*d:%*d %s %s %*[^-]- cgroup %*s %s", tmproot, tmpmount, tmpcgroups) != 3) { | |
continue; | |
} | |
while ((token = strsep(&cptr, ",")) != NULL) { | |
if (strcmp(token, "memory") == 0) { | |
memory = new CgroupMemorySubsystem(tmproot, tmpmount); | |
} else if (strcmp(token, "cpuset") == 0) { | |
cpuset = new CgroupSubsystem(tmproot, tmpmount); | |
} else if (strcmp(token, "cpu") == 0) { | |
cpu = new CgroupSubsystem(tmproot, tmpmount); | |
} else if (strcmp(token, "cpuacct") == 0) { | |
cpuacct= new CgroupSubsystem(tmproot, tmpmount); | |
} | |
} | |
} | |
fclose(mntinfo); | |
if (memory == NULL) { | |
log_debug(os, container)("Required cgroup memory subsystem not found"); | |
return; | |
} | |
if (cpuset == NULL) { | |
log_debug(os, container)("Required cgroup cpuset subsystem not found"); | |
return; | |
} | |
if (cpu == NULL) { | |
log_debug(os, container)("Required cgroup cpu subsystem not found"); | |
return; | |
} | |
if (cpuacct == NULL) { | |
log_debug(os, container)("Required cgroup cpuacct subsystem not found"); | |
return; | |
} | |
/* | |
* Read /proc/self/cgroup and map host mount point to | |
* local one via /proc/self/mountinfo content above | |
* | |
* Docker example: | |
* 5:memory:/docker/6558aed8fc662b194323ceab5b964f69cf36b3e8af877a14b80256e93aecb044 | |
* | |
* Host example: | |
* 5:memory:/user.slice | |
* | |
* Construct a path to the process specific memory and cpuset | |
* cgroup directory. | |
* | |
* For a container running under Docker from memory example above | |
* the paths would be: | |
* | |
* /sys/fs/cgroup/memory | |
* | |
* For a Host from memory example above the path would be: | |
* | |
* /sys/fs/cgroup/memory/user.slice | |
* | |
*/ | |
cgroup = fopen("/proc/self/cgroup", "r"); | |
if (cgroup == NULL) { | |
log_debug(os, container)("Can't open /proc/self/cgroup, %s", | |
os::strerror(errno)); | |
return; | |
} | |
while ((p = fgets(buf, MAXPATHLEN, cgroup)) != NULL) { | |
char *controllers; | |
char *token; | |
char *base; | |
/* Skip cgroup number */ | |
strsep(&p, ":"); | |
/* Get controllers and base */ | |
controllers = strsep(&p, ":"); | |
base = strsep(&p, "\n"); | |
if (controllers == NULL) { | |
continue; | |
} | |
while ((token = strsep(&controllers, ",")) != NULL) { | |
if (strcmp(token, "memory") == 0) { | |
memory->set_subsystem_path(base); | |
jlong hierarchy = uses_mem_hierarchy(); | |
if (hierarchy > 0) { | |
memory->set_hierarchical(true); | |
} | |
} else if (strcmp(token, "cpuset") == 0) { | |
cpuset->set_subsystem_path(base); | |
} else if (strcmp(token, "cpu") == 0) { | |
cpu->set_subsystem_path(base); | |
} else if (strcmp(token, "cpuacct") == 0) { | |
cpuacct->set_subsystem_path(base); | |
} | |
} | |
} | |
fclose(cgroup); | |
// We need to update the amount of physical memory now that | |
// command line arguments have been processed. | |
if ((mem_limit = memory_limit_in_bytes()) > 0) { | |
os::Linux::set_physical_memory(mem_limit); | |
log_info(os, container)("Memory Limit is: " JLONG_FORMAT, mem_limit); | |
} | |
_is_containerized = true; | |
} | |
const char * OSContainer::container_type() { | |
if (is_containerized()) { | |
return "cgroupv1"; | |
} else { | |
return NULL; | |
} | |
} | |
/* uses_mem_hierarchy | |
* | |
* Return whether or not hierarchical cgroup accounting is being | |
* done. | |
* | |
* return: | |
* A number > 0 if true, or | |
* OSCONTAINER_ERROR for not supported | |
*/ | |
jlong OSContainer::uses_mem_hierarchy() { | |
GET_CONTAINER_INFO(jlong, memory, "/memory.use_hierarchy", | |
"Use Hierarchy is: " JLONG_FORMAT, JLONG_FORMAT, use_hierarchy); | |
return use_hierarchy; | |
} | |
/* memory_limit_in_bytes | |
* | |
* Return the limit of available memory for this process. | |
* | |
* return: | |
* memory limit in bytes or | |
* -1 for unlimited | |
* OSCONTAINER_ERROR for not supported | |
*/ | |
jlong OSContainer::memory_limit_in_bytes() { | |
if (!memory->cache_has_expired()) { | |
return memory->memory_limit_in_bytes(); | |
} | |
jlong memory_limit = read_memory_limit_in_bytes(); | |
// Update CgroupMemorySubsystem to avoid re-reading container settings too often | |
memory->set_memory_limit_in_bytes(memory_limit); | |
return memory_limit; | |
} | |
jlong OSContainer::read_memory_limit_in_bytes() { | |
GET_CONTAINER_INFO(julong, memory, "/memory.limit_in_bytes", | |
"Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, memlimit); | |
if (memlimit >= _unlimited_memory) { | |
log_trace(os, container)("Non-Hierarchical Memory Limit is: Unlimited"); | |
if (memory->is_hierarchical()) { | |
const char* matchline = "hierarchical_memory_limit"; | |
char* format = "%s " JULONG_FORMAT; | |
GET_CONTAINER_INFO_LINE(julong, memory, "/memory.stat", matchline, | |
"Hierarchical Memory Limit is: " JULONG_FORMAT, format, hier_memlimit) | |
if (hier_memlimit >= _unlimited_memory) { | |
log_trace(os, container)("Hierarchical Memory Limit is: Unlimited"); | |
} else { | |
return (jlong)hier_memlimit; | |
} | |
} | |
return (jlong)-1; | |
} | |
else { | |
return (jlong)memlimit; | |
} | |
} | |
jlong OSContainer::memory_and_swap_limit_in_bytes() { | |
GET_CONTAINER_INFO(julong, memory, "/memory.memsw.limit_in_bytes", | |
"Memory and Swap Limit is: " JULONG_FORMAT, JULONG_FORMAT, memswlimit); | |
if (memswlimit >= _unlimited_memory) { | |
log_trace(os, container)("Non-Hierarchical Memory and Swap Limit is: Unlimited"); | |
if (memory->is_hierarchical()) { | |
const char* matchline = "hierarchical_memsw_limit"; | |
char* format = "%s " JULONG_FORMAT; | |
GET_CONTAINER_INFO_LINE(julong, memory, "/memory.stat", matchline, | |
"Hierarchical Memory and Swap Limit is : " JULONG_FORMAT, format, hier_memlimit) | |
if (hier_memlimit >= _unlimited_memory) { | |
log_trace(os, container)("Hierarchical Memory and Swap Limit is: Unlimited"); | |
} else { | |
return (jlong)hier_memlimit; | |
} | |
} | |
return (jlong)-1; | |
} else { | |
return (jlong)memswlimit; | |
} | |
} | |
jlong OSContainer::memory_soft_limit_in_bytes() { | |
GET_CONTAINER_INFO(julong, memory, "/memory.soft_limit_in_bytes", | |
"Memory Soft Limit is: " JULONG_FORMAT, JULONG_FORMAT, memsoftlimit); | |
if (memsoftlimit >= _unlimited_memory) { | |
log_trace(os, container)("Memory Soft Limit is: Unlimited"); | |
return (jlong)-1; | |
} else { | |
return (jlong)memsoftlimit; | |
} | |
} | |
/* memory_usage_in_bytes | |
* | |
* Return the amount of used memory for this process. | |
* | |
* return: | |
* memory usage in bytes or | |
* -1 for unlimited | |
* OSCONTAINER_ERROR for not supported | |
*/ | |
jlong OSContainer::memory_usage_in_bytes() { | |
GET_CONTAINER_INFO(jlong, memory, "/memory.usage_in_bytes", | |
"Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memusage); | |
return memusage; | |
} | |
/* memory_max_usage_in_bytes | |
* | |
* Return the maximum amount of used memory for this process. | |
* | |
* return: | |
* max memory usage in bytes or | |
* OSCONTAINER_ERROR for not supported | |
*/ | |
jlong OSContainer::memory_max_usage_in_bytes() { | |
GET_CONTAINER_INFO(jlong, memory, "/memory.max_usage_in_bytes", | |
"Maximum Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memmaxusage); | |
return memmaxusage; | |
} | |
/* active_processor_count | |
* | |
* Calculate an appropriate number of active processors for the | |
* VM to use based on these three inputs. | |
* | |
* cpu affinity | |
* cgroup cpu quota & cpu period | |
* cgroup cpu shares | |
* | |
* Algorithm: | |
* | |
* Determine the number of available CPUs from sched_getaffinity | |
* | |
* If user specified a quota (quota != -1), calculate the number of | |
* required CPUs by dividing quota by period. | |
* | |
* If shares are in effect (shares != -1), calculate the number | |
* of CPUs required for the shares by dividing the share value | |
* by PER_CPU_SHARES. | |
* | |
* All results of division are rounded up to the next whole number. | |
* | |
* If neither shares or quotas have been specified, return the | |
* number of active processors in the system. | |
* | |
* If both shares and quotas have been specified, the results are | |
* based on the flag PreferContainerQuotaForCPUCount. If true, | |
* return the quota value. If false return the smallest value | |
* between shares or quotas. | |
* | |
* If shares and/or quotas have been specified, the resulting number | |
* returned will never exceed the number of active processors. | |
* | |
* return: | |
* number of CPUs | |
*/ | |
int OSContainer::active_processor_count() { | |
int quota_count = 0, share_count = 0; | |
int cpu_count, limit_count; | |
int result; | |
// We use a cache with a timeout to avoid performing expensive | |
// computations in the event this function is called frequently. | |
// [See 8227006]. | |
if (!cpu->cache_has_expired()) { | |
log_trace(os, container)("OSContainer::active_processor_count (cached): %d", OSContainer::_active_processor_count); | |
return OSContainer::_active_processor_count; | |
} | |
cpu_count = limit_count = os::Linux::active_processor_count(); | |
int quota = cpu_quota(); | |
int period = cpu_period(); | |
int share = cpu_shares(); | |
if (quota > -1 && period > 0) { | |
quota_count = ceilf((float)quota / (float)period); | |
log_trace(os, container)("CPU Quota count based on quota/period: %d", quota_count); | |
} | |
if (share > -1) { | |
share_count = ceilf((float)share / (float)PER_CPU_SHARES); | |
log_trace(os, container)("CPU Share count based on shares: %d", share_count); | |
} | |
// If both shares and quotas are setup results depend | |
// on flag PreferContainerQuotaForCPUCount. | |
// If true, limit CPU count to quota | |
// If false, use minimum of shares and quotas | |
if (quota_count !=0 && share_count != 0) { | |
if (PreferContainerQuotaForCPUCount) { | |
limit_count = quota_count; | |
} else { | |
limit_count = MIN2(quota_count, share_count); | |
} | |
} else if (quota_count != 0) { | |
limit_count = quota_count; | |
} else if (share_count != 0) { | |
limit_count = share_count; | |
} | |
result = MIN2(cpu_count, limit_count); | |
log_trace(os, container)("OSContainer::active_processor_count: %d", result); | |
// Update the value and reset the cache timeout | |
OSContainer::_active_processor_count = result; | |
cpu->set_cache_expiry_time(OSCONTAINER_CACHE_TIMEOUT); | |
return result; | |
} | |
char * OSContainer::cpu_cpuset_cpus() { | |
GET_CONTAINER_INFO_CPTR(cptr, cpuset, "/cpuset.cpus", | |
"cpuset.cpus is: %s", "%1023s", cpus, 1024); | |
return os::strdup(cpus); | |
} | |
char * OSContainer::cpu_cpuset_memory_nodes() { | |
GET_CONTAINER_INFO_CPTR(cptr, cpuset, "/cpuset.mems", | |
"cpuset.mems is: %s", "%1023s", mems, 1024); | |
return os::strdup(mems); | |
} | |
/* cpu_quota | |
* | |
* Return the number of milliseconds per period | |
* process is guaranteed to run. | |
* | |
* return: | |
* quota time in milliseconds | |
* -1 for no quota | |
* OSCONTAINER_ERROR for not supported | |
*/ | |
int OSContainer::cpu_quota() { | |
GET_CONTAINER_INFO(int, cpu, "/cpu.cfs_quota_us", | |
"CPU Quota is: %d", "%d", quota); | |
return quota; | |
} | |
int OSContainer::cpu_period() { | |
GET_CONTAINER_INFO(int, cpu, "/cpu.cfs_period_us", | |
"CPU Period is: %d", "%d", period); | |
return period; | |
} | |
/* cpu_shares | |
* | |
* Return the amount of cpu shares available to the process | |
* | |
* return: | |
* Share number (typically a number relative to 1024) | |
* (2048 typically expresses 2 CPUs worth of processing) | |
* -1 for no share setup | |
* OSCONTAINER_ERROR for not supported | |
*/ | |
int OSContainer::cpu_shares() { | |
GET_CONTAINER_INFO(int, cpu, "/cpu.shares", | |
"CPU Shares is: %d", "%d", shares); | |
// Convert 1024 to no shares setup | |
if (shares == 1024) return -1; | |
return shares; | |
} | |