1 /*
2  * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  *
23  */
24 
25 #include <string.h>
26 #include <math.h>
27 #include <errno.h>
28 #include "utilities/globalDefinitions.hpp"
29 #include "memory/allocation.hpp"
30 #include "runtime/globals.hpp"
31 #include "runtime/os.hpp"
32 #include "logging/log.hpp"
33 #include "osContainer_linux.hpp"
34 
35 /*
36  * PER_CPU_SHARES has been set to 1024 because CPU shares' quota
37  * is commonly used in cloud frameworks like Kubernetes[1],
38  * AWS[2] and Mesos[3] in a similar way. They spawn containers with
39  * --cpu-shares option values scaled by PER_CPU_SHARES. Thus, we do
40  * the inverse for determining the number of possible available
41  * CPUs to the JVM inside a container. See JDK-8216366.
42  *
43  * [1] https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
44  *     In particular:
45  *        When using Docker:
46  *          The spec.containers[].resources.requests.cpu is converted to its core value, which is potentially
47  *          fractional, and multiplied by 1024. The greater of this number or 2 is used as the value of the
48  *          --cpu-shares flag in the docker run command.
49  * [2] https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDefinition.html
50  * [3] https://github.com/apache/mesos/blob/3478e344fb77d931f6122980c6e94cd3913c441d/src/docker/docker.cpp#L648
51  *     https://github.com/apache/mesos/blob/3478e344fb77d931f6122980c6e94cd3913c441d/src/slave/containerizer/mesos/isolators/cgroups/constants.hpp#L30
52  */
53 #define PER_CPU_SHARES 1024
54 
55 bool  OSContainer::_is_initialized   = false;
56 bool  OSContainer::_is_containerized = false;
57 int   OSContainer::_active_processor_count = 1;
58 julong _unlimited_memory;
59 
60 class CgroupSubsystem: CHeapObj<mtInternal> {
61  friend class OSContainer;
62 
63 
64  private:
65     volatile jlong _next_check_counter;
66 
67     /* mountinfo contents */
68     char *_root;
69     char *_mount_point;
70 
71     /* Constructed subsystem directory */
72     char *_path;
73 
74  public:
CgroupSubsystem(char * root,char * mountpoint)75     CgroupSubsystem(char *root, char *mountpoint) {
76       _root = os::strdup(root);
77       _mount_point = os::strdup(mountpoint);
78       _path = NULL;
79       _next_check_counter = min_jlong;
80     }
81 
82     /*
83      * Set directory to subsystem specific files based
84      * on the contents of the mountinfo and cgroup files.
85      */
set_subsystem_path(char * cgroup_path)86     void set_subsystem_path(char *cgroup_path) {
87       char buf[MAXPATHLEN+1];
88       if (_root != NULL && cgroup_path != NULL) {
89         if (strcmp(_root, "/") == 0) {
90           int buflen;
91           strncpy(buf, _mount_point, MAXPATHLEN);
92           buf[MAXPATHLEN-1] = '\0';
93           if (strcmp(cgroup_path,"/") != 0) {
94             buflen = strlen(buf);
95             if ((buflen + strlen(cgroup_path)) > (MAXPATHLEN-1)) {
96               return;
97             }
98             strncat(buf, cgroup_path, MAXPATHLEN-buflen);
99             buf[MAXPATHLEN-1] = '\0';
100           }
101           _path = os::strdup(buf);
102         } else {
103           if (strcmp(_root, cgroup_path) == 0) {
104             strncpy(buf, _mount_point, MAXPATHLEN);
105             buf[MAXPATHLEN-1] = '\0';
106             _path = os::strdup(buf);
107           } else {
108             char *p = strstr(cgroup_path, _root);
109             if (p != NULL && p == _root) {
110               if (strlen(cgroup_path) > strlen(_root)) {
111                 int buflen;
112                 strncpy(buf, _mount_point, MAXPATHLEN);
113                 buf[MAXPATHLEN-1] = '\0';
114                 buflen = strlen(buf);
115                 if ((buflen + strlen(cgroup_path) - strlen(_root)) > (MAXPATHLEN-1)) {
116                   return;
117                 }
118                 strncat(buf, cgroup_path + strlen(_root), MAXPATHLEN-buflen);
119                 buf[MAXPATHLEN-1] = '\0';
120                 _path = os::strdup(buf);
121               }
122             }
123           }
124         }
125       }
126     }
127 
subsystem_path()128     char *subsystem_path() { return _path; }
129 
cache_has_expired()130     bool cache_has_expired() {
131       return os::elapsed_counter() > _next_check_counter;
132     }
133 
set_cache_expiry_time(jlong timeout)134     void set_cache_expiry_time(jlong timeout) {
135       _next_check_counter = os::elapsed_counter() + timeout;
136     }
137 };
138 
139 class CgroupMemorySubsystem: CgroupSubsystem {
140  friend class OSContainer;
141 
142  private:
143     /* Some container runtimes set limits via cgroup
144      * hierarchy. If set to true consider also memory.stat
145      * file if everything else seems unlimited */
146     bool _uses_mem_hierarchy;
147     volatile jlong _memory_limit_in_bytes;
148 
149  public:
CgroupMemorySubsystem(char * root,char * mountpoint)150     CgroupMemorySubsystem(char *root, char *mountpoint) : CgroupSubsystem::CgroupSubsystem(root, mountpoint) {
151       _uses_mem_hierarchy = false;
152       _memory_limit_in_bytes = -1;
153 
154     }
155 
is_hierarchical()156     bool is_hierarchical() { return _uses_mem_hierarchy; }
set_hierarchical(bool value)157     void set_hierarchical(bool value) { _uses_mem_hierarchy = value; }
158 
memory_limit_in_bytes()159     jlong memory_limit_in_bytes() { return _memory_limit_in_bytes; }
set_memory_limit_in_bytes(jlong value)160     void set_memory_limit_in_bytes(jlong value) {
161       _memory_limit_in_bytes = value;
162       // max memory limit is unlikely to change, but we want to remain
163       // responsive to configuration changes. A very short grace time
164       // between re-read avoids excessive overhead during startup without
165       // significantly reducing the VMs ability to promptly react to reduced
166       // memory availability
167       set_cache_expiry_time(OSCONTAINER_CACHE_TIMEOUT);
168     }
169 
170 };
171 
172 CgroupMemorySubsystem* memory = NULL;
173 CgroupSubsystem* cpuset = NULL;
174 CgroupSubsystem* cpu = NULL;
175 CgroupSubsystem* cpuacct = NULL;
176 
177 typedef char * cptr;
178 
179 PRAGMA_DIAG_PUSH
180 PRAGMA_FORMAT_NONLITERAL_IGNORED
subsystem_file_line_contents(CgroupSubsystem * c,const char * filename,const char * matchline,const char * scan_fmt,T returnval)181 template <typename T> int subsystem_file_line_contents(CgroupSubsystem* c,
182                                               const char *filename,
183                                               const char *matchline,
184                                               const char *scan_fmt,
185                                               T returnval) {
186   FILE *fp = NULL;
187   char *p;
188   char file[MAXPATHLEN+1];
189   char buf[MAXPATHLEN+1];
190   char discard[MAXPATHLEN+1];
191   bool found_match = false;
192 
193   if (c == NULL) {
194     log_debug(os, container)("subsystem_file_line_contents: CgroupSubsytem* is NULL");
195     return OSCONTAINER_ERROR;
196   }
197   if (c->subsystem_path() == NULL) {
198     log_debug(os, container)("subsystem_file_line_contents: subsystem path is NULL");
199     return OSCONTAINER_ERROR;
200   }
201 
202   strncpy(file, c->subsystem_path(), MAXPATHLEN);
203   file[MAXPATHLEN-1] = '\0';
204   int filelen = strlen(file);
205   if ((filelen + strlen(filename)) > (MAXPATHLEN-1)) {
206     log_debug(os, container)("File path too long %s, %s", file, filename);
207     return OSCONTAINER_ERROR;
208   }
209   strncat(file, filename, MAXPATHLEN-filelen);
210   log_trace(os, container)("Path to %s is %s", filename, file);
211   fp = fopen(file, "r");
212   if (fp != NULL) {
213     int err = 0;
214     while ((p = fgets(buf, MAXPATHLEN, fp)) != NULL) {
215       found_match = false;
216       if (matchline == NULL) {
217         // single-line file case
218         int matched = sscanf(p, scan_fmt, returnval);
219         found_match = (matched == 1);
220       } else {
221         // multi-line file case
222         if (strstr(p, matchline) != NULL) {
223           // discard matchline string prefix
224           int matched = sscanf(p, scan_fmt, discard, returnval);
225           found_match = (matched == 2);
226         } else {
227           continue; // substring not found
228         }
229       }
230       if (found_match) {
231         fclose(fp);
232         return 0;
233       } else {
234         err = 1;
235         log_debug(os, container)("Type %s not found in file %s", scan_fmt, file);
236       }
237     }
238     if (err == 0) {
239       log_debug(os, container)("Empty file %s", file);
240     }
241   } else {
242     log_debug(os, container)("Open of file %s failed, %s", file, os::strerror(errno));
243   }
244   if (fp != NULL)
245     fclose(fp);
246   return OSCONTAINER_ERROR;
247 }
248 PRAGMA_DIAG_POP
249 
250 #define GET_CONTAINER_INFO(return_type, subsystem, filename,              \
251                            logstring, scan_fmt, variable)                 \
252   return_type variable;                                                   \
253 {                                                                         \
254   int err;                                                                \
255   err = subsystem_file_line_contents(subsystem,                           \
256                                      filename,                            \
257                                      NULL,                                \
258                                      scan_fmt,                            \
259                                      &variable);                          \
260   if (err != 0)                                                           \
261     return (return_type) OSCONTAINER_ERROR;                               \
262                                                                           \
263   log_trace(os, container)(logstring, variable);                          \
264 }
265 
266 #define GET_CONTAINER_INFO_CPTR(return_type, subsystem, filename,         \
267                                logstring, scan_fmt, variable, bufsize)    \
268   char variable[bufsize];                                                 \
269 {                                                                         \
270   int err;                                                                \
271   err = subsystem_file_line_contents(subsystem,                           \
272                                      filename,                            \
273                                      NULL,                                \
274                                      scan_fmt,                            \
275                                      variable);                           \
276   if (err != 0)                                                           \
277     return (return_type) NULL;                                            \
278                                                                           \
279   log_trace(os, container)(logstring, variable);                          \
280 }
281 
282 #define GET_CONTAINER_INFO_LINE(return_type, subsystem, filename,         \
283                            matchline, logstring, scan_fmt, variable)      \
284   return_type variable;                                                   \
285 {                                                                         \
286   int err;                                                                \
287   err = subsystem_file_line_contents(subsystem,                           \
288                                 filename,                                 \
289                                 matchline,                                \
290                                 scan_fmt,                                 \
291                                 &variable);                               \
292   if (err != 0)                                                           \
293     return (return_type) OSCONTAINER_ERROR;                               \
294                                                                           \
295   log_trace(os, container)(logstring, variable);                          \
296 }
297 
298 /* init
299  *
300  * Initialize the container support and determine if
301  * we are running under cgroup control.
302  */
init()303 void OSContainer::init() {
304   FILE *mntinfo = NULL;
305   FILE *cgroup = NULL;
306   char buf[MAXPATHLEN+1];
307   char tmproot[MAXPATHLEN+1];
308   char tmpmount[MAXPATHLEN+1];
309   char *p;
310   jlong mem_limit;
311 
312   assert(!_is_initialized, "Initializing OSContainer more than once");
313 
314   _is_initialized = true;
315   _is_containerized = false;
316 
317   _unlimited_memory = (LONG_MAX / os::vm_page_size()) * os::vm_page_size();
318 
319   log_trace(os, container)("OSContainer::init: Initializing Container Support");
320   if (!UseContainerSupport) {
321     log_trace(os, container)("Container Support not enabled");
322     return;
323   }
324 
325   /*
326    * Find the cgroup mount point for memory and cpuset
327    * by reading /proc/self/mountinfo
328    *
329    * Example for docker:
330    * 219 214 0:29 /docker/7208cebd00fa5f2e342b1094f7bed87fa25661471a4637118e65f1c995be8a34 /sys/fs/cgroup/memory ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory
331    *
332    * Example for host:
333    * 34 28 0:29 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,memory
334    */
335   mntinfo = fopen("/proc/self/mountinfo", "r");
336   if (mntinfo == NULL) {
337       log_debug(os, container)("Can't open /proc/self/mountinfo, %s",
338                                os::strerror(errno));
339       return;
340   }
341 
342   while ((p = fgets(buf, MAXPATHLEN, mntinfo)) != NULL) {
343     char tmpcgroups[MAXPATHLEN+1];
344     char *cptr = tmpcgroups;
345     char *token;
346 
347     // mountinfo format is documented at https://www.kernel.org/doc/Documentation/filesystems/proc.txt
348     if (sscanf(p, "%*d %*d %*d:%*d %s %s %*[^-]- cgroup %*s %s", tmproot, tmpmount, tmpcgroups) != 3) {
349       continue;
350     }
351     while ((token = strsep(&cptr, ",")) != NULL) {
352       if (strcmp(token, "memory") == 0) {
353         memory = new CgroupMemorySubsystem(tmproot, tmpmount);
354       } else if (strcmp(token, "cpuset") == 0) {
355         cpuset = new CgroupSubsystem(tmproot, tmpmount);
356       } else if (strcmp(token, "cpu") == 0) {
357         cpu = new CgroupSubsystem(tmproot, tmpmount);
358       } else if (strcmp(token, "cpuacct") == 0) {
359         cpuacct= new CgroupSubsystem(tmproot, tmpmount);
360       }
361     }
362   }
363 
364   fclose(mntinfo);
365 
366   if (memory == NULL) {
367     log_debug(os, container)("Required cgroup memory subsystem not found");
368     return;
369   }
370   if (cpuset == NULL) {
371     log_debug(os, container)("Required cgroup cpuset subsystem not found");
372     return;
373   }
374   if (cpu == NULL) {
375     log_debug(os, container)("Required cgroup cpu subsystem not found");
376     return;
377   }
378   if (cpuacct == NULL) {
379     log_debug(os, container)("Required cgroup cpuacct subsystem not found");
380     return;
381   }
382 
383   /*
384    * Read /proc/self/cgroup and map host mount point to
385    * local one via /proc/self/mountinfo content above
386    *
387    * Docker example:
388    * 5:memory:/docker/6558aed8fc662b194323ceab5b964f69cf36b3e8af877a14b80256e93aecb044
389    *
390    * Host example:
391    * 5:memory:/user.slice
392    *
393    * Construct a path to the process specific memory and cpuset
394    * cgroup directory.
395    *
396    * For a container running under Docker from memory example above
397    * the paths would be:
398    *
399    * /sys/fs/cgroup/memory
400    *
401    * For a Host from memory example above the path would be:
402    *
403    * /sys/fs/cgroup/memory/user.slice
404    *
405    */
406   cgroup = fopen("/proc/self/cgroup", "r");
407   if (cgroup == NULL) {
408     log_debug(os, container)("Can't open /proc/self/cgroup, %s",
409                              os::strerror(errno));
410     return;
411   }
412 
413   while ((p = fgets(buf, MAXPATHLEN, cgroup)) != NULL) {
414     char *controllers;
415     char *token;
416     char *base;
417 
418     /* Skip cgroup number */
419     strsep(&p, ":");
420     /* Get controllers and base */
421     controllers = strsep(&p, ":");
422     base = strsep(&p, "\n");
423 
424     if (controllers == NULL) {
425       continue;
426     }
427 
428     while ((token = strsep(&controllers, ",")) != NULL) {
429       if (strcmp(token, "memory") == 0) {
430         memory->set_subsystem_path(base);
431         jlong hierarchy = uses_mem_hierarchy();
432         if (hierarchy > 0) {
433           memory->set_hierarchical(true);
434         }
435       } else if (strcmp(token, "cpuset") == 0) {
436         cpuset->set_subsystem_path(base);
437       } else if (strcmp(token, "cpu") == 0) {
438         cpu->set_subsystem_path(base);
439       } else if (strcmp(token, "cpuacct") == 0) {
440         cpuacct->set_subsystem_path(base);
441       }
442     }
443   }
444 
445   fclose(cgroup);
446 
447   // We need to update the amount of physical memory now that
448   // command line arguments have been processed.
449   if ((mem_limit = memory_limit_in_bytes()) > 0) {
450     os::Linux::set_physical_memory(mem_limit);
451     log_info(os, container)("Memory Limit is: " JLONG_FORMAT, mem_limit);
452   }
453 
454   _is_containerized = true;
455 
456 }
457 
container_type()458 const char * OSContainer::container_type() {
459   if (is_containerized()) {
460     return "cgroupv1";
461   } else {
462     return NULL;
463   }
464 }
465 
466 /* uses_mem_hierarchy
467  *
468  * Return whether or not hierarchical cgroup accounting is being
469  * done.
470  *
471  * return:
472  *    A number > 0 if true, or
473  *    OSCONTAINER_ERROR for not supported
474  */
uses_mem_hierarchy()475 jlong OSContainer::uses_mem_hierarchy() {
476   GET_CONTAINER_INFO(jlong, memory, "/memory.use_hierarchy",
477                     "Use Hierarchy is: " JLONG_FORMAT, JLONG_FORMAT, use_hierarchy);
478   return use_hierarchy;
479 }
480 
481 
482 /* memory_limit_in_bytes
483  *
484  * Return the limit of available memory for this process.
485  *
486  * return:
487  *    memory limit in bytes or
488  *    -1 for unlimited
489  *    OSCONTAINER_ERROR for not supported
490  */
memory_limit_in_bytes()491 jlong OSContainer::memory_limit_in_bytes() {
492   if (!memory->cache_has_expired()) {
493     return memory->memory_limit_in_bytes();
494   }
495   jlong memory_limit = read_memory_limit_in_bytes();
496   // Update CgroupMemorySubsystem to avoid re-reading container settings too often
497   memory->set_memory_limit_in_bytes(memory_limit);
498   return memory_limit;
499 }
500 
read_memory_limit_in_bytes()501 jlong OSContainer::read_memory_limit_in_bytes() {
502   GET_CONTAINER_INFO(julong, memory, "/memory.limit_in_bytes",
503                      "Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, memlimit);
504 
505   if (memlimit >= _unlimited_memory) {
506     log_trace(os, container)("Non-Hierarchical Memory Limit is: Unlimited");
507     if (memory->is_hierarchical()) {
508       const char* matchline = "hierarchical_memory_limit";
509       const char* format = "%s " JULONG_FORMAT;
510       GET_CONTAINER_INFO_LINE(julong, memory, "/memory.stat", matchline,
511                              "Hierarchical Memory Limit is: " JULONG_FORMAT, format, hier_memlimit)
512       if (hier_memlimit >= _unlimited_memory) {
513         log_trace(os, container)("Hierarchical Memory Limit is: Unlimited");
514       } else {
515         return (jlong)hier_memlimit;
516       }
517     }
518     return (jlong)-1;
519   }
520   else {
521     return (jlong)memlimit;
522   }
523 }
524 
memory_and_swap_limit_in_bytes()525 jlong OSContainer::memory_and_swap_limit_in_bytes() {
526   GET_CONTAINER_INFO(julong, memory, "/memory.memsw.limit_in_bytes",
527                      "Memory and Swap Limit is: " JULONG_FORMAT, JULONG_FORMAT, memswlimit);
528   if (memswlimit >= _unlimited_memory) {
529     log_trace(os, container)("Non-Hierarchical Memory and Swap Limit is: Unlimited");
530     if (memory->is_hierarchical()) {
531       const char* matchline = "hierarchical_memsw_limit";
532       const char* format = "%s " JULONG_FORMAT;
533       GET_CONTAINER_INFO_LINE(julong, memory, "/memory.stat", matchline,
534                              "Hierarchical Memory and Swap Limit is : " JULONG_FORMAT, format, hier_memlimit)
535       if (hier_memlimit >= _unlimited_memory) {
536         log_trace(os, container)("Hierarchical Memory and Swap Limit is: Unlimited");
537       } else {
538         return (jlong)hier_memlimit;
539       }
540     }
541     return (jlong)-1;
542   } else {
543     return (jlong)memswlimit;
544   }
545 }
546 
memory_soft_limit_in_bytes()547 jlong OSContainer::memory_soft_limit_in_bytes() {
548   GET_CONTAINER_INFO(julong, memory, "/memory.soft_limit_in_bytes",
549                      "Memory Soft Limit is: " JULONG_FORMAT, JULONG_FORMAT, memsoftlimit);
550   if (memsoftlimit >= _unlimited_memory) {
551     log_trace(os, container)("Memory Soft Limit is: Unlimited");
552     return (jlong)-1;
553   } else {
554     return (jlong)memsoftlimit;
555   }
556 }
557 
558 /* memory_usage_in_bytes
559  *
560  * Return the amount of used memory for this process.
561  *
562  * return:
563  *    memory usage in bytes or
564  *    -1 for unlimited
565  *    OSCONTAINER_ERROR for not supported
566  */
memory_usage_in_bytes()567 jlong OSContainer::memory_usage_in_bytes() {
568   GET_CONTAINER_INFO(jlong, memory, "/memory.usage_in_bytes",
569                      "Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memusage);
570   return memusage;
571 }
572 
573 /* memory_max_usage_in_bytes
574  *
575  * Return the maximum amount of used memory for this process.
576  *
577  * return:
578  *    max memory usage in bytes or
579  *    OSCONTAINER_ERROR for not supported
580  */
memory_max_usage_in_bytes()581 jlong OSContainer::memory_max_usage_in_bytes() {
582   GET_CONTAINER_INFO(jlong, memory, "/memory.max_usage_in_bytes",
583                      "Maximum Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memmaxusage);
584   return memmaxusage;
585 }
586 
587 /* active_processor_count
588  *
589  * Calculate an appropriate number of active processors for the
590  * VM to use based on these three inputs.
591  *
592  * cpu affinity
593  * cgroup cpu quota & cpu period
594  * cgroup cpu shares
595  *
596  * Algorithm:
597  *
598  * Determine the number of available CPUs from sched_getaffinity
599  *
600  * If user specified a quota (quota != -1), calculate the number of
601  * required CPUs by dividing quota by period.
602  *
603  * If shares are in effect (shares != -1), calculate the number
604  * of CPUs required for the shares by dividing the share value
605  * by PER_CPU_SHARES.
606  *
607  * All results of division are rounded up to the next whole number.
608  *
609  * If neither shares or quotas have been specified, return the
610  * number of active processors in the system.
611  *
612  * If both shares and quotas have been specified, the results are
613  * based on the flag PreferContainerQuotaForCPUCount.  If true,
614  * return the quota value.  If false return the smallest value
615  * between shares or quotas.
616  *
617  * If shares and/or quotas have been specified, the resulting number
618  * returned will never exceed the number of active processors.
619  *
620  * return:
621  *    number of CPUs
622  */
active_processor_count()623 int OSContainer::active_processor_count() {
624   int quota_count = 0, share_count = 0;
625   int cpu_count, limit_count;
626   int result;
627 
628   // We use a cache with a timeout to avoid performing expensive
629   // computations in the event this function is called frequently.
630   // [See 8227006].
631   if (!cpu->cache_has_expired()) {
632     log_trace(os, container)("OSContainer::active_processor_count (cached): %d", OSContainer::_active_processor_count);
633     return OSContainer::_active_processor_count;
634   }
635 
636   cpu_count = limit_count = os::Linux::active_processor_count();
637   int quota  = cpu_quota();
638   int period = cpu_period();
639   int share  = cpu_shares();
640 
641   if (quota > -1 && period > 0) {
642     quota_count = ceilf((float)quota / (float)period);
643     log_trace(os, container)("CPU Quota count based on quota/period: %d", quota_count);
644   }
645   if (share > -1) {
646     share_count = ceilf((float)share / (float)PER_CPU_SHARES);
647     log_trace(os, container)("CPU Share count based on shares: %d", share_count);
648   }
649 
650   // If both shares and quotas are setup results depend
651   // on flag PreferContainerQuotaForCPUCount.
652   // If true, limit CPU count to quota
653   // If false, use minimum of shares and quotas
654   if (quota_count !=0 && share_count != 0) {
655     if (PreferContainerQuotaForCPUCount) {
656       limit_count = quota_count;
657     } else {
658       limit_count = MIN2(quota_count, share_count);
659     }
660   } else if (quota_count != 0) {
661     limit_count = quota_count;
662   } else if (share_count != 0) {
663     limit_count = share_count;
664   }
665 
666   result = MIN2(cpu_count, limit_count);
667   log_trace(os, container)("OSContainer::active_processor_count: %d", result);
668 
669   // Update the value and reset the cache timeout
670   OSContainer::_active_processor_count = result;
671   cpu->set_cache_expiry_time(OSCONTAINER_CACHE_TIMEOUT);
672 
673   return result;
674 }
675 
cpu_cpuset_cpus()676 char * OSContainer::cpu_cpuset_cpus() {
677   GET_CONTAINER_INFO_CPTR(cptr, cpuset, "/cpuset.cpus",
678                      "cpuset.cpus is: %s", "%1023s", cpus, 1024);
679   return os::strdup(cpus);
680 }
681 
cpu_cpuset_memory_nodes()682 char * OSContainer::cpu_cpuset_memory_nodes() {
683   GET_CONTAINER_INFO_CPTR(cptr, cpuset, "/cpuset.mems",
684                      "cpuset.mems is: %s", "%1023s", mems, 1024);
685   return os::strdup(mems);
686 }
687 
688 /* cpu_quota
689  *
690  * Return the number of milliseconds per period
691  * process is guaranteed to run.
692  *
693  * return:
694  *    quota time in milliseconds
695  *    -1 for no quota
696  *    OSCONTAINER_ERROR for not supported
697  */
cpu_quota()698 int OSContainer::cpu_quota() {
699   GET_CONTAINER_INFO(int, cpu, "/cpu.cfs_quota_us",
700                      "CPU Quota is: %d", "%d", quota);
701   return quota;
702 }
703 
cpu_period()704 int OSContainer::cpu_period() {
705   GET_CONTAINER_INFO(int, cpu, "/cpu.cfs_period_us",
706                      "CPU Period is: %d", "%d", period);
707   return period;
708 }
709 
710 /* cpu_shares
711  *
712  * Return the amount of cpu shares available to the process
713  *
714  * return:
715  *    Share number (typically a number relative to 1024)
716  *                 (2048 typically expresses 2 CPUs worth of processing)
717  *    -1 for no share setup
718  *    OSCONTAINER_ERROR for not supported
719  */
cpu_shares()720 int OSContainer::cpu_shares() {
721   GET_CONTAINER_INFO(int, cpu, "/cpu.shares",
722                      "CPU Shares is: %d", "%d", shares);
723   // Convert 1024 to no shares setup
724   if (shares == 1024) return -1;
725 
726   return shares;
727 }
728