changeset 58366:d527da8f8f9b

8239785: Cgroups: Incorrect detection logic on old systems in hotspot Summary: Return NULL subsystem if no cgroup controllers are mounted. Reviewed-by: bobv, mbaesken
author sgehwolf
date Tue, 25 Feb 2020 12:17:26 +0100
parents a9a78d821f37
children 7898edac8a27
files src/hotspot/os/linux/cgroupSubsystem_linux.cpp src/hotspot/os/linux/cgroupSubsystem_linux.hpp src/hotspot/share/prims/whitebox.cpp src/hotspot/share/prims/whitebox.hpp test/hotspot/jtreg/containers/cgroup/CgroupSubsystemFactory.java test/lib/sun/hotspot/WhiteBox.java
diffstat 6 files changed, 660 insertions(+), 230 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp	Thu Mar 12 16:42:03 2020 +0300
+++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp	Tue Feb 25 12:17:26 2020 +0100
@@ -39,237 +39,34 @@
   CgroupV1Controller* cpuset = NULL;
   CgroupV1Controller* cpu = NULL;
   CgroupV1Controller* cpuacct = NULL;
-  FILE *mntinfo = NULL;
-  FILE *cgroups = NULL;
-  FILE *cgroup = NULL;
-  char buf[MAXPATHLEN+1];
-  char tmproot[MAXPATHLEN+1];
-  char tmpmount[MAXPATHLEN+1];
-  char *p;
-  bool is_cgroupsV2;
-  // true iff all controllers, memory, cpu, cpuset, cpuacct are enabled
-  // at the kernel level.
-  bool all_controllers_enabled;
+  CgroupInfo cg_infos[CG_INFO_LENGTH];
+  u1 cg_type_flags = INVALID_CGROUPS_GENERIC;
+  const char* proc_cgroups = "/proc/cgroups";
+  const char* proc_self_cgroup = "/proc/self/cgroup";
+  const char* proc_self_mountinfo = "/proc/self/mountinfo";
 
-  CgroupInfo cg_infos[CG_INFO_LENGTH];
-  int cpuset_idx  = 0;
-  int cpu_idx     = 1;
-  int cpuacct_idx = 2;
-  int memory_idx  = 3;
+  bool valid_cgroup = determine_type(cg_infos, proc_cgroups, proc_self_cgroup, proc_self_mountinfo, &cg_type_flags);
 
-  /*
-   * Read /proc/cgroups so as to be able to distinguish cgroups v2 vs cgroups v1.
-   *
-   * For cgroups v1 unified hierarchy, cpu, cpuacct, cpuset, memory controllers
-   * must have non-zero for the hierarchy ID field.
-   */
-  cgroups = fopen("/proc/cgroups", "r");
-  if (cgroups == NULL) {
-      log_debug(os, container)("Can't open /proc/cgroups, %s",
-                               os::strerror(errno));
-      return NULL;
-  }
-
-  while ((p = fgets(buf, MAXPATHLEN, cgroups)) != NULL) {
-    char name[MAXPATHLEN+1];
-    int  hierarchy_id;
-    int  enabled;
-
-    // Format of /proc/cgroups documented via man 7 cgroups
-    if (sscanf(p, "%s %d %*d %d", name, &hierarchy_id, &enabled) != 3) {
-      continue;
-    }
-    if (strcmp(name, "memory") == 0) {
-      cg_infos[memory_idx]._name = os::strdup(name);
-      cg_infos[memory_idx]._hierarchy_id = hierarchy_id;
-      cg_infos[memory_idx]._enabled = (enabled == 1);
-    } else if (strcmp(name, "cpuset") == 0) {
-      cg_infos[cpuset_idx]._name = os::strdup(name);
-      cg_infos[cpuset_idx]._hierarchy_id = hierarchy_id;
-      cg_infos[cpuset_idx]._enabled = (enabled == 1);
-    } else if (strcmp(name, "cpu") == 0) {
-      cg_infos[cpu_idx]._name = os::strdup(name);
-      cg_infos[cpu_idx]._hierarchy_id = hierarchy_id;
-      cg_infos[cpu_idx]._enabled = (enabled == 1);
-    } else if (strcmp(name, "cpuacct") == 0) {
-      cg_infos[cpuacct_idx]._name = os::strdup(name);
-      cg_infos[cpuacct_idx]._hierarchy_id = hierarchy_id;
-      cg_infos[cpuacct_idx]._enabled = (enabled == 1);
-    }
-  }
-  fclose(cgroups);
-
-  is_cgroupsV2 = true;
-  all_controllers_enabled = true;
-  for (int i = 0; i < CG_INFO_LENGTH; i++) {
-    is_cgroupsV2 = is_cgroupsV2 && cg_infos[i]._hierarchy_id == 0;
-    all_controllers_enabled = all_controllers_enabled && cg_infos[i]._enabled;
-  }
-
-  if (!all_controllers_enabled) {
-    // one or more controllers disabled, disable container support
-    log_debug(os, container)("One or more required controllers disabled at kernel level.");
+  if (!valid_cgroup) {
+    // Could not detect cgroup type
     return NULL;
   }
+  assert(is_valid_cgroup(&cg_type_flags), "Expected valid cgroup type");
 
-  /*
-   * Read /proc/self/cgroup and determine:
-   *  - the cgroup path for cgroups v2 or
-   *  - on a cgroups v1 system, collect info for mapping
-   *    the host mount point to the local one via /proc/self/mountinfo below.
-   */
-  cgroup = fopen("/proc/self/cgroup", "r");
-  if (cgroup == NULL) {
-    log_debug(os, container)("Can't open /proc/self/cgroup, %s",
-                             os::strerror(errno));
-    return NULL;
-  }
-
-  while ((p = fgets(buf, MAXPATHLEN, cgroup)) != NULL) {
-    char *controllers;
-    char *token;
-    char *hierarchy_id_str;
-    int  hierarchy_id;
-    char *cgroup_path;
-
-    hierarchy_id_str = strsep(&p, ":");
-    hierarchy_id = atoi(hierarchy_id_str);
-    /* Get controllers and base */
-    controllers = strsep(&p, ":");
-    cgroup_path = strsep(&p, "\n");
-
-    if (controllers == NULL) {
-      continue;
-    }
-
-    while (!is_cgroupsV2 && (token = strsep(&controllers, ",")) != NULL) {
-      if (strcmp(token, "memory") == 0) {
-        assert(hierarchy_id == cg_infos[memory_idx]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch");
-        cg_infos[memory_idx]._cgroup_path = os::strdup(cgroup_path);
-      } else if (strcmp(token, "cpuset") == 0) {
-        assert(hierarchy_id == cg_infos[cpuset_idx]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch");
-        cg_infos[cpuset_idx]._cgroup_path = os::strdup(cgroup_path);
-      } else if (strcmp(token, "cpu") == 0) {
-        assert(hierarchy_id == cg_infos[cpu_idx]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch");
-        cg_infos[cpu_idx]._cgroup_path = os::strdup(cgroup_path);
-      } else if (strcmp(token, "cpuacct") == 0) {
-        assert(hierarchy_id == cg_infos[cpuacct_idx]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch");
-        cg_infos[cpuacct_idx]._cgroup_path = os::strdup(cgroup_path);
-      }
-    }
-    if (is_cgroupsV2) {
-      for (int i = 0; i < CG_INFO_LENGTH; i++) {
-        cg_infos[i]._cgroup_path = os::strdup(cgroup_path);
-      }
-    }
-  }
-  fclose(cgroup);
-
-  if (is_cgroupsV2) {
-    // Find the cgroup2 mount point by reading /proc/self/mountinfo
-    mntinfo = fopen("/proc/self/mountinfo", "r");
-    if (mntinfo == NULL) {
-        log_debug(os, container)("Can't open /proc/self/mountinfo, %s",
-                                 os::strerror(errno));
-        return NULL;
-    }
-
-    char cgroupv2_mount[MAXPATHLEN+1];
-    char fstype[MAXPATHLEN+1];
-    bool mount_point_found = false;
-    while ((p = fgets(buf, MAXPATHLEN, mntinfo)) != NULL) {
-      char *tmp_mount_point = cgroupv2_mount;
-      char *tmp_fs_type = fstype;
-
-      // mountinfo format is documented at https://www.kernel.org/doc/Documentation/filesystems/proc.txt
-      if (sscanf(p, "%*d %*d %*d:%*d %*s %s %*[^-]- %s cgroup2 %*s", tmp_mount_point, tmp_fs_type) == 2) {
-        // we likely have an early match return, be sure we have cgroup2 as fstype
-        if (strcmp("cgroup2", tmp_fs_type) == 0) {
-          mount_point_found = true;
-          break;
-        }
-      }
-    }
-    fclose(mntinfo);
-    if (!mount_point_found) {
-      log_trace(os, container)("Mount point for cgroupv2 not found in /proc/self/mountinfo");
-      return NULL;
-    }
+  if (is_cgroup_v2(&cg_type_flags)) {
     // Cgroups v2 case, we have all the info we need.
     // Construct the subsystem, free resources and return
     // Note: any index in cg_infos will do as the path is the same for
     //       all controllers.
-    CgroupController* unified = new CgroupV2Controller(cgroupv2_mount, cg_infos[memory_idx]._cgroup_path);
-    for (int i = 0; i < CG_INFO_LENGTH; i++) {
-      os::free(cg_infos[i]._name);
-      os::free(cg_infos[i]._cgroup_path);
-    }
+    CgroupController* unified = new CgroupV2Controller(cg_infos[MEMORY_IDX]._mount_path, cg_infos[MEMORY_IDX]._cgroup_path);
     log_debug(os, container)("Detected cgroups v2 unified hierarchy");
+    cleanup(cg_infos);
     return new CgroupV2Subsystem(unified);
   }
 
-  // What follows is cgroups v1
-  log_debug(os, container)("Detected cgroups hybrid or legacy hierarchy, using cgroups v1 controllers");
-
   /*
-   * Find the cgroup mount point for memory and cpuset
-   * by reading /proc/self/mountinfo
+   * Cgroup v1 case:
    *
-   * 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 NULL;
-  }
-
-  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 CgroupV1MemoryController(tmproot, tmpmount);
-      } else if (strcmp(token, "cpuset") == 0) {
-        cpuset = new CgroupV1Controller(tmproot, tmpmount);
-      } else if (strcmp(token, "cpu") == 0) {
-        cpu = new CgroupV1Controller(tmproot, tmpmount);
-      } else if (strcmp(token, "cpuacct") == 0) {
-        cpuacct= new CgroupV1Controller(tmproot, tmpmount);
-      }
-    }
-  }
-
-  fclose(mntinfo);
-
-  if (memory == NULL) {
-    log_debug(os, container)("Required cgroup v1 memory subsystem not found");
-    return NULL;
-  }
-  if (cpuset == NULL) {
-    log_debug(os, container)("Required cgroup v1 cpuset subsystem not found");
-    return NULL;
-  }
-  if (cpu == NULL) {
-    log_debug(os, container)("Required cgroup v1 cpu subsystem not found");
-    return NULL;
-  }
-  if (cpuacct == NULL) {
-    log_debug(os, container)("Required cgroup v1 cpuacct subsystem not found");
-    return NULL;
-  }
-
-  /*
    * Use info gathered previously from /proc/self/cgroup
    * and map host mount point to
    * local one via /proc/self/mountinfo content above
@@ -293,21 +90,302 @@
    * /sys/fs/cgroup/memory/user.slice
    *
    */
+  assert(is_cgroup_v1(&cg_type_flags), "Cgroup v1 expected");
   for (int i = 0; i < CG_INFO_LENGTH; i++) {
     CgroupInfo info = cg_infos[i];
     if (strcmp(info._name, "memory") == 0) {
+      memory = new CgroupV1MemoryController(info._root_mount_path, info._mount_path);
       memory->set_subsystem_path(info._cgroup_path);
     } else if (strcmp(info._name, "cpuset") == 0) {
+      cpuset = new CgroupV1Controller(info._root_mount_path, info._mount_path);
       cpuset->set_subsystem_path(info._cgroup_path);
     } else if (strcmp(info._name, "cpu") == 0) {
+      cpu = new CgroupV1Controller(info._root_mount_path, info._mount_path);
       cpu->set_subsystem_path(info._cgroup_path);
     } else if (strcmp(info._name, "cpuacct") == 0) {
+      cpuacct = new CgroupV1Controller(info._root_mount_path, info._mount_path);
       cpuacct->set_subsystem_path(info._cgroup_path);
     }
   }
+  cleanup(cg_infos);
   return new CgroupV1Subsystem(cpuset, cpu, cpuacct, memory);
 }
 
+bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos,
+                                            const char* proc_cgroups,
+                                            const char* proc_self_cgroup,
+                                            const char* proc_self_mountinfo,
+                                            u1* flags) {
+  FILE *mntinfo = NULL;
+  FILE *cgroups = NULL;
+  FILE *cgroup = NULL;
+  char buf[MAXPATHLEN+1];
+  char *p;
+  bool is_cgroupsV2;
+  // true iff all controllers, memory, cpu, cpuset, cpuacct are enabled
+  // at the kernel level.
+  bool all_controllers_enabled;
+
+  /*
+   * Read /proc/cgroups so as to be able to distinguish cgroups v2 vs cgroups v1.
+   *
+   * For cgroups v1 hierarchy (hybrid or legacy), cpu, cpuacct, cpuset, memory controllers
+   * must have non-zero for the hierarchy ID field and relevant controllers mounted.
+   * Conversely, for cgroups v2 (unified hierarchy), cpu, cpuacct, cpuset, memory
+   * controllers must have hierarchy ID 0 and the unified controller mounted.
+   */
+  cgroups = fopen(proc_cgroups, "r");
+  if (cgroups == NULL) {
+      log_debug(os, container)("Can't open %s, %s",
+                               proc_cgroups, os::strerror(errno));
+      *flags = INVALID_CGROUPS_GENERIC;
+      return false;
+  }
+
+  while ((p = fgets(buf, MAXPATHLEN, cgroups)) != NULL) {
+    char name[MAXPATHLEN+1];
+    int  hierarchy_id;
+    int  enabled;
+
+    // Format of /proc/cgroups documented via man 7 cgroups
+    if (sscanf(p, "%s %d %*d %d", name, &hierarchy_id, &enabled) != 3) {
+      continue;
+    }
+    if (strcmp(name, "memory") == 0) {
+      cg_infos[MEMORY_IDX]._name = os::strdup(name);
+      cg_infos[MEMORY_IDX]._hierarchy_id = hierarchy_id;
+      cg_infos[MEMORY_IDX]._enabled = (enabled == 1);
+    } else if (strcmp(name, "cpuset") == 0) {
+      cg_infos[CPUSET_IDX]._name = os::strdup(name);
+      cg_infos[CPUSET_IDX]._hierarchy_id = hierarchy_id;
+      cg_infos[CPUSET_IDX]._enabled = (enabled == 1);
+    } else if (strcmp(name, "cpu") == 0) {
+      cg_infos[CPU_IDX]._name = os::strdup(name);
+      cg_infos[CPU_IDX]._hierarchy_id = hierarchy_id;
+      cg_infos[CPU_IDX]._enabled = (enabled == 1);
+    } else if (strcmp(name, "cpuacct") == 0) {
+      cg_infos[CPUACCT_IDX]._name = os::strdup(name);
+      cg_infos[CPUACCT_IDX]._hierarchy_id = hierarchy_id;
+      cg_infos[CPUACCT_IDX]._enabled = (enabled == 1);
+    }
+  }
+  fclose(cgroups);
+
+  is_cgroupsV2 = true;
+  all_controllers_enabled = true;
+  for (int i = 0; i < CG_INFO_LENGTH; i++) {
+    is_cgroupsV2 = is_cgroupsV2 && cg_infos[i]._hierarchy_id == 0;
+    all_controllers_enabled = all_controllers_enabled && cg_infos[i]._enabled;
+  }
+
+  if (!all_controllers_enabled) {
+    // one or more controllers disabled, disable container support
+    log_debug(os, container)("One or more required controllers disabled at kernel level.");
+    cleanup(cg_infos);
+    *flags = INVALID_CGROUPS_GENERIC;
+    return false;
+  }
+
+  /*
+   * Read /proc/self/cgroup and determine:
+   *  - the cgroup path for cgroups v2 or
+   *  - on a cgroups v1 system, collect info for mapping
+   *    the host mount point to the local one via /proc/self/mountinfo below.
+   */
+  cgroup = fopen(proc_self_cgroup, "r");
+  if (cgroup == NULL) {
+    log_debug(os, container)("Can't open %s, %s",
+                             proc_self_cgroup, os::strerror(errno));
+    cleanup(cg_infos);
+    *flags = INVALID_CGROUPS_GENERIC;
+    return false;
+  }
+
+  while ((p = fgets(buf, MAXPATHLEN, cgroup)) != NULL) {
+    char *controllers;
+    char *token;
+    char *hierarchy_id_str;
+    int  hierarchy_id;
+    char *cgroup_path;
+
+    hierarchy_id_str = strsep(&p, ":");
+    hierarchy_id = atoi(hierarchy_id_str);
+    /* Get controllers and base */
+    controllers = strsep(&p, ":");
+    cgroup_path = strsep(&p, "\n");
+
+    if (controllers == NULL) {
+      continue;
+    }
+
+    while (!is_cgroupsV2 && (token = strsep(&controllers, ",")) != NULL) {
+      if (strcmp(token, "memory") == 0) {
+        assert(hierarchy_id == cg_infos[MEMORY_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch");
+        cg_infos[MEMORY_IDX]._cgroup_path = os::strdup(cgroup_path);
+      } else if (strcmp(token, "cpuset") == 0) {
+        assert(hierarchy_id == cg_infos[CPUSET_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch");
+        cg_infos[CPUSET_IDX]._cgroup_path = os::strdup(cgroup_path);
+      } else if (strcmp(token, "cpu") == 0) {
+        assert(hierarchy_id == cg_infos[CPU_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch");
+        cg_infos[CPU_IDX]._cgroup_path = os::strdup(cgroup_path);
+      } else if (strcmp(token, "cpuacct") == 0) {
+        assert(hierarchy_id == cg_infos[CPUACCT_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch");
+        cg_infos[CPUACCT_IDX]._cgroup_path = os::strdup(cgroup_path);
+      }
+    }
+    if (is_cgroupsV2) {
+      for (int i = 0; i < CG_INFO_LENGTH; i++) {
+        cg_infos[i]._cgroup_path = os::strdup(cgroup_path);
+      }
+    }
+  }
+  fclose(cgroup);
+
+  // Find various mount points by reading /proc/self/mountinfo
+  // mountinfo format is documented at https://www.kernel.org/doc/Documentation/filesystems/proc.txt
+  mntinfo = fopen(proc_self_mountinfo, "r");
+  if (mntinfo == NULL) {
+      log_debug(os, container)("Can't open %s, %s",
+                               proc_self_mountinfo, os::strerror(errno));
+      cleanup(cg_infos);
+      *flags = INVALID_CGROUPS_GENERIC;
+      return false;
+  }
+
+  bool cgroupv2_mount_point_found = false;
+  bool any_cgroup_mounts_found = false;
+  while ((p = fgets(buf, MAXPATHLEN, mntinfo)) != NULL) {
+    char tmp_mount_point[MAXPATHLEN+1];
+    char tmp_fs_type[MAXPATHLEN+1];
+    char tmproot[MAXPATHLEN+1];
+    char tmpmount[MAXPATHLEN+1];
+    char tmpcgroups[MAXPATHLEN+1];
+    char *cptr = tmpcgroups;
+    char *token;
+
+    // Cgroup v2 relevant info. We only look for the _mount_path iff is_cgroupsV2 so
+    // as to avoid memory stomping of the _mount_path pointer later on in the cgroup v1
+    // block in the hybrid case.
+    //
+    if (is_cgroupsV2 && sscanf(p, "%*d %*d %*d:%*d %*s %s %*[^-]- %s cgroup2 %*s", tmp_mount_point, tmp_fs_type) == 2) {
+      // we likely have an early match return (e.g. cgroup fs match), be sure we have cgroup2 as fstype
+      if (!cgroupv2_mount_point_found && strcmp("cgroup2", tmp_fs_type) == 0) {
+        cgroupv2_mount_point_found = true;
+        any_cgroup_mounts_found = true;
+        for (int i = 0; i < CG_INFO_LENGTH; i++) {
+          assert(cg_infos[i]._mount_path == NULL, "_mount_path memory stomping");
+          cg_infos[i]._mount_path = os::strdup(tmp_mount_point);
+        }
+      }
+    }
+
+    /* Cgroup v1 relevant info
+     *
+     * Find the cgroup mount point for memory, cpuset, cpu, cpuacct
+     *
+     * 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
+     */
+    if (sscanf(p, "%*d %*d %*d:%*d %s %s %*[^-]- %s cgroup %s", tmproot, tmpmount, tmp_fs_type, tmpcgroups) == 4) {
+      if (strcmp("cgroup", tmp_fs_type) != 0) {
+        // Skip cgroup2 fs lines on hybrid or unified hierarchy.
+        continue;
+      }
+      any_cgroup_mounts_found = true;
+      while ((token = strsep(&cptr, ",")) != NULL) {
+        if (strcmp(token, "memory") == 0) {
+          assert(cg_infos[MEMORY_IDX]._mount_path == NULL, "stomping of _mount_path");
+          cg_infos[MEMORY_IDX]._mount_path = os::strdup(tmpmount);
+          cg_infos[MEMORY_IDX]._root_mount_path = os::strdup(tmproot);
+          cg_infos[MEMORY_IDX]._data_complete = true;
+        } else if (strcmp(token, "cpuset") == 0) {
+          assert(cg_infos[CPUSET_IDX]._mount_path == NULL, "stomping of _mount_path");
+          cg_infos[CPUSET_IDX]._mount_path = os::strdup(tmpmount);
+          cg_infos[CPUSET_IDX]._root_mount_path = os::strdup(tmproot);
+          cg_infos[CPUSET_IDX]._data_complete = true;
+        } else if (strcmp(token, "cpu") == 0) {
+          assert(cg_infos[CPU_IDX]._mount_path == NULL, "stomping of _mount_path");
+          cg_infos[CPU_IDX]._mount_path = os::strdup(tmpmount);
+          cg_infos[CPU_IDX]._root_mount_path = os::strdup(tmproot);
+          cg_infos[CPU_IDX]._data_complete = true;
+        } else if (strcmp(token, "cpuacct") == 0) {
+          assert(cg_infos[CPUACCT_IDX]._mount_path == NULL, "stomping of _mount_path");
+          cg_infos[CPUACCT_IDX]._mount_path = os::strdup(tmpmount);
+          cg_infos[CPUACCT_IDX]._root_mount_path = os::strdup(tmproot);
+          cg_infos[CPUACCT_IDX]._data_complete = true;
+        }
+      }
+    }
+  }
+  fclose(mntinfo);
+
+  // Neither cgroup2 nor cgroup filesystems mounted via /proc/self/mountinfo
+  // No point in continuing.
+  if (!any_cgroup_mounts_found) {
+    log_trace(os, container)("No cgroup controllers mounted.");
+    cleanup(cg_infos);
+    *flags = INVALID_CGROUPS_NO_MOUNT;
+    return false;
+  }
+
+  if (is_cgroupsV2) {
+    if (!cgroupv2_mount_point_found) {
+      log_trace(os, container)("Mount point for cgroupv2 not found in /proc/self/mountinfo");
+      cleanup(cg_infos);
+      *flags = INVALID_CGROUPS_V2;
+      return false;
+    }
+    // Cgroups v2 case, we have all the info we need.
+    *flags = CGROUPS_V2;
+    return true;
+  }
+
+  // What follows is cgroups v1
+  log_debug(os, container)("Detected cgroups hybrid or legacy hierarchy, using cgroups v1 controllers");
+
+  if (!cg_infos[MEMORY_IDX]._data_complete) {
+    log_debug(os, container)("Required cgroup v1 memory subsystem not found");
+    cleanup(cg_infos);
+    *flags = INVALID_CGROUPS_V1;
+    return false;
+  }
+  if (!cg_infos[CPUSET_IDX]._data_complete) {
+    log_debug(os, container)("Required cgroup v1 cpuset subsystem not found");
+    cleanup(cg_infos);
+    *flags = INVALID_CGROUPS_V1;
+    return false;
+  }
+  if (!cg_infos[CPU_IDX]._data_complete) {
+    log_debug(os, container)("Required cgroup v1 cpu subsystem not found");
+    cleanup(cg_infos);
+    *flags = INVALID_CGROUPS_V1;
+    return false;
+  }
+  if (!cg_infos[CPUACCT_IDX]._data_complete) {
+    log_debug(os, container)("Required cgroup v1 cpuacct subsystem not found");
+    cleanup(cg_infos);
+    *flags = INVALID_CGROUPS_V1;
+    return false;
+  }
+  // Cgroups v1 case, we have all the info we need.
+  *flags = CGROUPS_V1;
+  return true;
+
+};
+
+void CgroupSubsystemFactory::cleanup(CgroupInfo* cg_infos) {
+  assert(cg_infos != NULL, "Invariant");
+  for (int i = 0; i < CG_INFO_LENGTH; i++) {
+    os::free(cg_infos[i]._name);
+    os::free(cg_infos[i]._cgroup_path);
+    os::free(cg_infos[i]._root_mount_path);
+    os::free(cg_infos[i]._mount_path);
+  }
+}
+
 /* active_processor_count
  *
  * Calculate an appropriate number of active processors for the
--- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp	Thu Mar 12 16:42:03 2020 +0300
+++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp	Tue Feb 25 12:17:26 2020 +0100
@@ -54,6 +54,20 @@
  */
 #define PER_CPU_SHARES 1024
 
+#define CGROUPS_V1               1
+#define CGROUPS_V2               2
+#define INVALID_CGROUPS_V2       3
+#define INVALID_CGROUPS_V1       4
+#define INVALID_CGROUPS_NO_MOUNT 5
+#define INVALID_CGROUPS_GENERIC  6
+
+// Four controllers: cpu, cpuset, cpuacct, memory
+#define CG_INFO_LENGTH 4
+#define CPUSET_IDX     0
+#define CPU_IDX        1
+#define CPUACCT_IDX    2
+#define MEMORY_IDX     3
+
 typedef char * cptr;
 
 class CgroupController: public CHeapObj<mtInternal> {
@@ -180,8 +194,6 @@
   log_trace(os, container)(logstring, variable);                          \
 }
 
-// Four controllers: cpu, cpuset, cpuacct, memory
-#define CG_INFO_LENGTH 4
 
 class CachedMetric : public CHeapObj<mtInternal>{
   private:
@@ -242,23 +254,62 @@
     virtual CachingCgroupController* cpu_controller() = 0;
 };
 
-class CgroupSubsystemFactory: AllStatic {
-  public:
-    static CgroupSubsystem* create();
-};
-
-// Class representing info in /proc/self/cgroup.
-// See man 7 cgroups
+// Utility class for storing info retrieved from /proc/cgroups,
+// /proc/self/cgroup and /proc/self/mountinfo
+// For reference see man 7 cgroups and CgroupSubsystemFactory
 class CgroupInfo : public StackObj {
   friend class CgroupSubsystemFactory;
+  friend class WhiteBox;
 
   private:
-  char* _name;
-  int _hierarchy_id;
-  bool _enabled;
-  char* _cgroup_path;
+    char* _name;
+    int _hierarchy_id;
+    bool _enabled;
+    bool _data_complete;    // indicating cgroup v1 data is complete for this controller
+    char* _cgroup_path;     // cgroup controller path from /proc/self/cgroup
+    char* _root_mount_path; // root mount path from /proc/self/mountinfo. Unused for cgroup v2
+    char* _mount_path;      // mount path from /proc/self/mountinfo.
+
+  public:
+    CgroupInfo() {
+      _name = NULL;
+      _hierarchy_id = -1;
+      _enabled = false;
+      _data_complete = false;
+      _cgroup_path = NULL;
+      _root_mount_path = NULL;
+      _mount_path = NULL;
+    }
 
 };
 
+class CgroupSubsystemFactory: AllStatic {
+  friend class WhiteBox;
+
+  public:
+    static CgroupSubsystem* create();
+  private:
+    static inline bool is_cgroup_v2(u1* flags) {
+       return *flags == CGROUPS_V2;
+    }
+
+#ifdef ASSERT
+    static inline bool is_valid_cgroup(u1* flags) {
+       return *flags == CGROUPS_V1 || *flags == CGROUPS_V2;
+    }
+    static inline bool is_cgroup_v1(u1* flags) {
+       return *flags == CGROUPS_V1;
+    }
+#endif
+
+    // Determine the cgroup type (version 1 or version 2), given
+    // relevant paths to files. Sets 'flags' accordingly.
+    static bool determine_type(CgroupInfo* cg_infos,
+                               const char* proc_cgroups,
+                               const char* proc_self_cgroup,
+                               const char* proc_self_mountinfo,
+                               u1* flags);
+    static void cleanup(CgroupInfo* cg_infos);
+};
 
 #endif // CGROUP_SUBSYSTEM_LINUX_HPP
--- a/src/hotspot/share/prims/whitebox.cpp	Thu Mar 12 16:42:03 2020 +0300
+++ b/src/hotspot/share/prims/whitebox.cpp	Tue Feb 25 12:17:26 2020 +0100
@@ -107,6 +107,7 @@
 
 #ifdef LINUX
 #include "osContainer_linux.hpp"
+#include "cgroupSubsystem_linux.hpp"
 #endif
 
 #define SIZE_T_MAX_VALUE ((size_t) -1)
@@ -1013,6 +1014,18 @@
   return result;
 WB_END
 
+#ifdef LINUX
+bool WhiteBox::validate_cgroup(const char* proc_cgroups,
+                               const char* proc_self_cgroup,
+                               const char* proc_self_mountinfo,
+                               u1* cg_flags) {
+  CgroupInfo cg_infos[4];
+  return CgroupSubsystemFactory::determine_type(cg_infos, proc_cgroups,
+                                                    proc_self_cgroup,
+                                                    proc_self_mountinfo, cg_flags);
+}
+#endif
+
 bool WhiteBox::compile_method(Method* method, int comp_level, int bci, Thread* THREAD) {
   // Screen for unavailable/bad comp level or null method
   AbstractCompiler* comp = CompileBroker::compiler(comp_level);
@@ -2189,6 +2202,31 @@
   return false;
 WB_END
 
+WB_ENTRY(jint, WB_ValidateCgroup(JNIEnv* env,
+                                    jobject o,
+                                    jstring proc_cgroups,
+                                    jstring proc_self_cgroup,
+                                    jstring proc_self_mountinfo))
+  jint ret = 0;
+#ifdef LINUX
+  ThreadToNativeFromVM ttnfv(thread);
+  const char* p_cgroups = env->GetStringUTFChars(proc_cgroups, NULL);
+  CHECK_JNI_EXCEPTION_(env, 0);
+  const char* p_s_cgroup = env->GetStringUTFChars(proc_self_cgroup, NULL);
+  CHECK_JNI_EXCEPTION_(env, 0);
+  const char* p_s_mountinfo = env->GetStringUTFChars(proc_self_mountinfo, NULL);
+  CHECK_JNI_EXCEPTION_(env, 0);
+  u1 cg_type_flags = 0;
+  // This sets cg_type_flags
+  WhiteBox::validate_cgroup(p_cgroups, p_s_cgroup, p_s_mountinfo, &cg_type_flags);
+  ret = (jint)cg_type_flags;
+  env->ReleaseStringUTFChars(proc_cgroups, p_cgroups);
+  env->ReleaseStringUTFChars(proc_self_cgroup, p_s_cgroup);
+  env->ReleaseStringUTFChars(proc_self_mountinfo, p_s_mountinfo);
+#endif
+  return ret;
+WB_END
+
 WB_ENTRY(void, WB_PrintOsInfo(JNIEnv* env, jobject o))
   os::print_os_info(tty);
 WB_END
@@ -2462,6 +2500,9 @@
   {CC"checkLibSpecifiesNoexecstack", CC"(Ljava/lang/String;)Z",
                                                       (void*)&WB_CheckLibSpecifiesNoexecstack},
   {CC"isContainerized",           CC"()Z",            (void*)&WB_IsContainerized },
+  {CC"validateCgroup",
+      CC"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+                                                      (void*)&WB_ValidateCgroup },
   {CC"printOsInfo",               CC"()V",            (void*)&WB_PrintOsInfo },
   {CC"disableElfSectionCache",    CC"()V",            (void*)&WB_DisableElfSectionCache },
   {CC"resolvedMethodItemsCount",  CC"()J",            (void*)&WB_ResolvedMethodItemsCount },
--- a/src/hotspot/share/prims/whitebox.hpp	Thu Mar 12 16:42:03 2020 +0300
+++ b/src/hotspot/share/prims/whitebox.hpp	Tue Feb 25 12:17:26 2020 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2020, 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
@@ -68,6 +68,9 @@
     JNINativeMethod* method_array, int method_count);
   static void register_extended(JNIEnv* env, jclass wbclass, JavaThread* thread);
   static bool compile_method(Method* method, int comp_level, int bci, Thread* THREAD);
+#ifdef LINUX
+  static bool validate_cgroup(const char* proc_cgroups, const char* proc_self_cgroup, const char* proc_self_mountinfo, u1* cg_flags);
+#endif
 };
 
 #endif // SHARE_PRIMS_WHITEBOX_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/containers/cgroup/CgroupSubsystemFactory.java	Tue Feb 25 12:17:26 2020 +0100
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2020, Red Hat Inc.
+ * 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.
+ */
+
+/*
+ * @test CgroupSubsystemFactory
+ * @requires os.family == "linux"
+ * @library /testlibrary /test/lib
+ * @build sun.hotspot.WhiteBox
+ * @run driver ClassFileInstaller sun.hotspot.WhiteBox
+ *                              sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI CgroupSubsystemFactory
+ */
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.Utils;
+import jdk.test.lib.util.FileUtils;
+import sun.hotspot.WhiteBox;
+
+/*
+ * Verify hotspot's detection heuristics of CgroupSubsystemFactory::create()
+ */
+public class CgroupSubsystemFactory {
+
+    // Mirrored from src/hotspot/os/linux/cgroupSubsystem_linux.hpp
+    private static final int CGROUPS_V1 = 1;
+    private static final int CGROUPS_V2 = 2;
+    private static final int INVALID_CGROUPS_V2 = 3;
+    private static final int INVALID_CGROUPS_V1 = 4;
+    private static final int INVALID_CGROUPS_NO_MOUNT = 5;
+    private Path existingDirectory;
+    private Path cgroupv1CgInfoZeroHierarchy;
+    private Path cgroupv1MntInfoZeroHierarchy;
+    private Path cgroupv2CgInfoZeroHierarchy;
+    private Path cgroupv2MntInfoZeroHierarchy;
+    private Path cgroupv1CgInfoNonZeroHierarchy;
+    private Path cgroupv1MntInfoNonZeroHierarchyOtherOrder;
+    private Path cgroupv1MntInfoNonZeroHierarchy;
+    private String mntInfoEmpty = "";
+    private Path cgroupV1SelfCgroup;
+    private Path cgroupV2SelfCgroup;
+    private Path cgroupV2MntInfoMissingCgroupv2;
+    private Path cgroupv1MntInfoMissingMemoryController;
+    private String procSelfCgroupHybridContent = "11:hugetlb:/\n" +
+            "10:devices:/user.slice\n" +
+            "9:pids:/user.slice/user-15263.slice/user@15263.service\n" +
+            "8:cpu,cpuacct:/\n" +
+            "7:perf_event:/\n" +
+            "6:freezer:/\n" +
+            "5:blkio:/\n" +
+            "4:net_cls,net_prio:/\n" +
+            "3:cpuset:/\n" +
+            "2:memory:/user.slice/user-15263.slice/user@15263.service\n" +
+            "1:name=systemd:/user.slice/user-15263.slice/user@15263.service/gnome-terminal-server.service\n" +
+            "0::/user.slice/user-15263.slice/user@15263.service/gnome-terminal-server.service";
+    private String procSelfCgroupV2UnifiedContent = "0::/user.slice/user-1000.slice/session-3.scope";
+    private String cgroupsZeroHierarchy =
+            "#subsys_name hierarchy num_cgroups enabled\n" +
+            "cpuset 0 1 1\n" +
+            "cpu 0 1 1\n" +
+            "cpuacct 0 1 1\n" +
+            "memory 0 1 1\n" +
+            "devices 0 1 1\n" +
+            "freezer 0 1 1\n" +
+            "net_cls 0 1 1\n" +
+            "blkio 0 1 1\n" +
+            "perf_event 0 1 1 ";
+    private String cgroupV2LineHybrid = "31 30 0:27 / /sys/fs/cgroup/unified rw,nosuid,nodev,noexec,relatime shared:5 - cgroup2 cgroup2 rw,seclabel,nsdelegate\n";
+    private String cgroupv1MountInfoLineMemory = "35 30 0:31 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:7 - cgroup cgroup rw,seclabel,memory\n";
+    private String mntInfoHybridStub =
+            "30 23 0:26 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:4 - tmpfs tmpfs ro,seclabel,mode=755\n" +
+            "32 30 0:28 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:6 - cgroup cgroup rw,seclabel,xattr,name=systemd\n" +
+            "36 30 0:32 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:8 - cgroup cgroup rw,seclabel,pids\n" +
+            "37 30 0:33 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:9 - cgroup cgroup rw,seclabel,perf_event\n" +
+            "38 30 0:34 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,seclabel,net_cls,net_prio\n" +
+            "39 30 0:35 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:11 - cgroup cgroup rw,seclabel,hugetlb\n" +
+            "40 30 0:36 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:12 - cgroup cgroup rw,seclabel,cpu,cpuacct\n" +
+            "41 30 0:37 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:13 - cgroup cgroup rw,seclabel,devices\n" +
+            "42 30 0:38 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:14 - cgroup cgroup rw,seclabel,cpuset\n" +
+            "43 30 0:39 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:15 - cgroup cgroup rw,seclabel,blkio\n" +
+            "44 30 0:40 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,seclabel,freezer";
+    private String mntInfoHybridRest = cgroupv1MountInfoLineMemory + mntInfoHybridStub;
+    private String mntInfoHybridMissingMemory = mntInfoHybridStub;
+    private String mntInfoHybrid = cgroupV2LineHybrid + mntInfoHybridRest;
+    private String mntInfoHybridFlippedOrder = mntInfoHybridRest + cgroupV2LineHybrid;
+    private String cgroupsNonZeroHierarchy =
+            "#subsys_name hierarchy   num_cgroups enabled\n" +
+            "cpuset  3   1   1\n" +
+            "cpu 8   1   1\n" +
+            "cpuacct 8   1   1\n" +
+            "blkio   10  1   1\n" +
+            "memory  2   90  1\n" +
+            "devices 8   74  1\n" +
+            "freezer 11  1   1\n" +
+            "net_cls 5   1   1\n" +
+            "perf_event  4   1   1\n" +
+            "net_prio    5   1   1\n" +
+            "hugetlb 6   1   1\n" +
+            "pids    3   80  1";
+    private String mntInfoCgroupsV2Only =
+            "28 21 0:25 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:4 - cgroup2 cgroup2 rw,seclabel,nsdelegate";
+
+    private void setup() {
+        try {
+            existingDirectory = Utils.createTempDirectory(CgroupSubsystemFactory.class.getSimpleName());
+            Path cgroupsZero = Paths.get(existingDirectory.toString(), "cgroups_zero");
+            Files.writeString(cgroupsZero, cgroupsZeroHierarchy, StandardCharsets.UTF_8);
+            cgroupv1CgInfoZeroHierarchy = cgroupsZero;
+            cgroupv2CgInfoZeroHierarchy = cgroupsZero;
+            cgroupv1MntInfoZeroHierarchy = Paths.get(existingDirectory.toString(), "mountinfo_empty");
+            Files.writeString(cgroupv1MntInfoZeroHierarchy, mntInfoEmpty);
+
+            cgroupv2MntInfoZeroHierarchy = Paths.get(existingDirectory.toString(), "mountinfo_cgroupv2");
+            Files.writeString(cgroupv2MntInfoZeroHierarchy, mntInfoCgroupsV2Only);
+
+            cgroupv1CgInfoNonZeroHierarchy = Paths.get(existingDirectory.toString(), "cgroups_non_zero");
+            Files.writeString(cgroupv1CgInfoNonZeroHierarchy, cgroupsNonZeroHierarchy);
+
+            cgroupv1MntInfoNonZeroHierarchy = Paths.get(existingDirectory.toString(), "mountinfo_non_zero");
+            Files.writeString(cgroupv1MntInfoNonZeroHierarchy, mntInfoHybrid);
+
+            cgroupv1MntInfoNonZeroHierarchyOtherOrder = Paths.get(existingDirectory.toString(), "mountinfo_non_zero_cgroupv2_last");
+            Files.writeString(cgroupv1MntInfoNonZeroHierarchyOtherOrder, mntInfoHybridFlippedOrder);
+
+            cgroupV1SelfCgroup = Paths.get(existingDirectory.toString(), "cgroup_self_hybrid");
+            Files.writeString(cgroupV1SelfCgroup, procSelfCgroupHybridContent);
+
+            cgroupV2SelfCgroup = Paths.get(existingDirectory.toString(), "cgroup_self_v2");
+            Files.writeString(cgroupV2SelfCgroup, procSelfCgroupV2UnifiedContent);
+
+            cgroupv1MntInfoMissingMemoryController = Paths.get(existingDirectory.toString(), "mnt_info_missing_memory");
+            Files.writeString(cgroupv1MntInfoMissingMemoryController, mntInfoHybridMissingMemory);
+
+            cgroupV2MntInfoMissingCgroupv2 = Paths.get(existingDirectory.toString(), "mnt_info_missing_cgroup2");
+            Files.writeString(cgroupV2MntInfoMissingCgroupv2, mntInfoHybridStub);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void teardown() {
+        try {
+            FileUtils.deleteFileTreeWithRetry(existingDirectory);
+        } catch (IOException e) {
+            System.err.println("Teardown failed. " + e.getMessage());
+        }
+    }
+
+    private boolean isValidCgroup(int value) {
+        return value == CGROUPS_V1 || value == CGROUPS_V2;
+    }
+
+    public void testCgroupv1NoMounts(WhiteBox wb) {
+        String procCgroups = cgroupv1CgInfoZeroHierarchy.toString();
+        String procSelfCgroup = cgroupV1SelfCgroup.toString();
+        String procSelfMountinfo = cgroupv1MntInfoZeroHierarchy.toString();
+        int retval = wb.validateCgroup(procCgroups, procSelfCgroup, procSelfMountinfo);
+        Asserts.assertEQ(INVALID_CGROUPS_NO_MOUNT, retval, "No cgroups mounted in /proc/self/mountinfo. Invalid.");
+        Asserts.assertFalse(isValidCgroup(retval));
+        System.out.println("testCgroupv1NoMounts PASSED!");
+    }
+
+    public void testCgroupv2NoCgroup2Fs(WhiteBox wb) {
+        String procCgroups = cgroupv2CgInfoZeroHierarchy.toString();
+        String procSelfCgroup = cgroupV2SelfCgroup.toString();
+        String procSelfMountinfo = cgroupV2MntInfoMissingCgroupv2.toString();
+        int retval = wb.validateCgroup(procCgroups, procSelfCgroup, procSelfMountinfo);
+        Asserts.assertEQ(INVALID_CGROUPS_V2, retval, "No cgroup2 filesystem in /proc/self/mountinfo. Invalid.");
+        Asserts.assertFalse(isValidCgroup(retval));
+        System.out.println("testCgroupv2NoCgroup2Fs PASSED!");
+    }
+
+    public void testCgroupv1MissingMemoryController(WhiteBox wb) {
+        String procCgroups = cgroupv1CgInfoNonZeroHierarchy.toString();
+        String procSelfCgroup = cgroupV1SelfCgroup.toString();
+        String procSelfMountinfo = cgroupv1MntInfoMissingMemoryController.toString();
+        int retval = wb.validateCgroup(procCgroups, procSelfCgroup, procSelfMountinfo);
+        Asserts.assertEQ(INVALID_CGROUPS_V1, retval, "Required memory controller path missing in mountinfo. Invalid.");
+        Asserts.assertFalse(isValidCgroup(retval));
+        System.out.println("testCgroupv1MissingMemoryController PASSED!");
+    }
+
+    public void testCgroupv2(WhiteBox wb) {
+        String procCgroups = cgroupv2CgInfoZeroHierarchy.toString();
+        String procSelfCgroup = cgroupV2SelfCgroup.toString();
+        String procSelfMountinfo = cgroupv2MntInfoZeroHierarchy.toString();
+        int retval = wb.validateCgroup(procCgroups, procSelfCgroup, procSelfMountinfo);
+        Asserts.assertEQ(CGROUPS_V2, retval, "Expected");
+        Asserts.assertTrue(isValidCgroup(retval));
+        System.out.println("testCgroupv2 PASSED!");
+    }
+
+    public void testCgroupV1Hybrid(WhiteBox wb) {
+        String procCgroups = cgroupv1CgInfoNonZeroHierarchy.toString();
+        String procSelfCgroup = cgroupV1SelfCgroup.toString();
+        String procSelfMountinfo = cgroupv1MntInfoNonZeroHierarchy.toString();
+        int retval = wb.validateCgroup(procCgroups, procSelfCgroup, procSelfMountinfo);
+        Asserts.assertEQ(CGROUPS_V1, retval, "Hybrid cgroups expected as cgroups v1");
+        Asserts.assertTrue(isValidCgroup(retval));
+        System.out.println("testCgroupv1Hybrid PASSED!");
+    }
+
+    public void testCgroupV1HybridMntInfoOrder(WhiteBox wb) {
+        String procCgroups = cgroupv1CgInfoNonZeroHierarchy.toString();
+        String procSelfCgroup = cgroupV1SelfCgroup.toString();
+        String procSelfMountinfo = cgroupv1MntInfoNonZeroHierarchyOtherOrder.toString();
+        int retval = wb.validateCgroup(procCgroups, procSelfCgroup, procSelfMountinfo);
+        Asserts.assertEQ(CGROUPS_V1, retval, "Hybrid cgroups expected as cgroups v1");
+        Asserts.assertTrue(isValidCgroup(retval));
+        System.out.println("testCgroupv1HybridMntInfoOrder PASSED!");
+    }
+
+
+    public static void main(String[] args) throws Exception {
+        WhiteBox wb = WhiteBox.getWhiteBox();
+        CgroupSubsystemFactory test = new CgroupSubsystemFactory();
+        test.setup();
+        try {
+            test.testCgroupv1NoMounts(wb);
+            test.testCgroupv2(wb);
+            test.testCgroupV1Hybrid(wb);
+            test.testCgroupV1HybridMntInfoOrder(wb);
+            test.testCgroupv1MissingMemoryController(wb);
+            test.testCgroupv2NoCgroup2Fs(wb);
+        } finally {
+            test.teardown();
+        }
+    }
+}
--- a/test/lib/sun/hotspot/WhiteBox.java	Thu Mar 12 16:42:03 2020 +0300
+++ b/test/lib/sun/hotspot/WhiteBox.java	Tue Feb 25 12:17:26 2020 +0100
@@ -595,6 +595,9 @@
 
   // Container testing
   public native boolean isContainerized();
+  public native int validateCgroup(String procCgroups,
+                                   String procSelfCgroup,
+                                   String procSelfMountinfo);
   public native void printOsInfo();
 
   // Decoder