Skip to content

Commit 8b2e8fd

Browse files
Add internal getCGroups2CpuSet() for querying CGroups v2 settings 'cpuset.cpus' and 'cpuset.cpus.effective' [#141]
1 parent cdaa11f commit 8b2e8fd

File tree

3 files changed

+104
-2
lines changed

3 files changed

+104
-2
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Package: parallelly
2-
Version: 1.46.1-9013
2+
Version: 1.46.1-9014
33
Title: Enhancing the 'parallel' Package
44
Imports:
55
parallel,

R/cgroups.R

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ withCGroups <- function(tarball, expr = NULL, envir = parent.frame(), tmpdir = N
289289
fcns <- list(
290290
getCGroupsMounts, getCGroups, getCGroupsVersion,
291291
getCGroups1CpuSet, getCGroups1CpuPeriodMicroseconds, getCGroups1CpuQuota,
292-
getCGroups2CpuMax
292+
getCGroups2CpuSet, getCGroups2CpuMax
293293
)
294294
for (fcn in fcns) {
295295
environment(fcn)$.cache <- NULL
@@ -339,6 +339,8 @@ withCGroups <- function(tarball, expr = NULL, envir = parent.frame(), tmpdir = N
339339

340340
message(" - length(getCGroups1CpuSet()): ", length(getCGroups1CpuSet()))
341341
message(" - getCGroups1CpuQuota(): ", getCGroups1CpuQuota())
342+
message(" - length(getCGroups2CpuSet('cpuset.cpus')): ", length(getCGroups2CpuSet("cpuset.cpus")))
343+
message(" - length(getCGroups2CpuSet('cpuset.cpus.effective')): ", length(getCGroups2CpuSet("cpuset.cpus.effective")))
342344
message(" - getCGroups2CpuMax(): ", getCGroups2CpuMax())
343345

344346
message(" - availableCores(which = 'all'):")
@@ -858,6 +860,87 @@ getCGroups1CpuQuota <- local({
858860
# --------------------------------------------------------------------------
859861
# CGroups v2 CPU settings
860862
# --------------------------------------------------------------------------
863+
# Get cgroups v2 'cpuset.cpus'
864+
#
865+
# @return An integer vector of CPU indices. If cgroups v2 field
866+
# `cpuset.cpus` could not be queried, integer(0) is returned.
867+
#
868+
# From 'Control Group v2' documentation [1]:
869+
#
870+
# `cpuset.cpus`:
871+
# A read-write multiple values file which exists on non-root
872+
# cgroups.
873+
#
874+
# It lists the requested CPUs to be used by tasks within this
875+
# cgroup. The effective CPUs is described by `cpuset.cpus.effective`.
876+
#
877+
# The format is the same as the cpuset.cpus file in cgroup v1.
878+
#
879+
# [1] https://docs.kernel.org/admin-guide/cgroup-v2.html
880+
#
881+
getCGroups2CpuSet <- local({
882+
.cache <- list()
883+
884+
function(name = c("cpuset.cpus", "cpuset.cpus.effective")) {
885+
name <- match.arg(name)
886+
887+
res <- .cache[[name]]
888+
if (!is.null(res)) return(res)
889+
890+
## TEMPORARY: In case the cgroups options causes problems, make
891+
## it possible to override their values via hidden options
892+
cpuset <- get_package_option(paste0("cgroups2.", name), NULL)
893+
if (!is.null(cpuset)) return(cpuset)
894+
895+
## e.g. /sys/fs/cgroup/cpuset.cpus or /sys/fs/cgroup/cpuset.cpus.effective
896+
value0 <- getCGroups2Value(name)
897+
if (is.na(value0)) {
898+
res <- integer(0L)
899+
.cache[[name]] <<- res
900+
return(res)
901+
}
902+
903+
## Parse 0-63; 0-7,9; 0-7,10-12; etc.
904+
code <- gsub("-", ":", value0, fixed = TRUE)
905+
code <- sprintf("c(%s)", code)
906+
expr <- tryCatch({
907+
parse(text = code)
908+
}, error = function(ex) {
909+
warning(sprintf("Syntax error parsing cgroups v2 %s: %s", sQuote(name), sQuote(value0)))
910+
integer(0L)
911+
})
912+
913+
value <- tryCatch({
914+
suppressWarnings(as.integer(eval(expr)))
915+
}, error = function(ex) {
916+
warning(sprintf("Failed to parse cgroups v2 %s: %s", sQuote(name), sQuote(value0)))
917+
integer(0L)
918+
})
919+
920+
## Sanity checks
921+
max_cores <- maxCores()
922+
if (any(value < 0L | value >= max_cores)) {
923+
warning(sprintf("[INTERNAL]: Will ignore the cgroups v2 CPU set, because it contains one or more CPU indices that is out of range [0,%d]: %s", max_cores - 1L, value0))
924+
value <- integer(0L)
925+
}
926+
927+
if (any(duplicated(value))) {
928+
warning(sprintf("[INTERNAL]: Detected and dropped duplicated CPU indices in the cgroups v2 CPU set: %s", value0))
929+
value <- unique(value)
930+
}
931+
932+
cpuset <- value
933+
934+
## Should never happen, but just in case
935+
stop_if_not(length(cpuset) <= max_cores)
936+
937+
.cache[[name]] <<- cpuset
938+
939+
cpuset
940+
}
941+
})
942+
943+
861944
# @return A non-negative numeric.
862945
# If cgroups is not in use, or could not be queried, NA_real_ is returned.
863946
#

inst/testme/test-cgroups.R

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ options(parallelly.cgroups.cpuquota = 0:3)
2525
message("getCGroups1CpuQuota(): ", parallelly:::getCGroups1CpuQuota())
2626
options(parallelly.cgroups.cpuquota = NULL)
2727

28+
options(parallelly.cgroups2.cpuset.cpus = 0:3)
29+
message("getCGroups2CpuSet(): ", parallelly:::getCGroups2CpuSet())
30+
message("getCGroups2CpuSet('cpuset.cpus'): ", parallelly:::getCGroups2CpuSet("cpuset.cpus"))
31+
options(parallelly.cgroups2.cpuset.cpus = NULL)
32+
33+
options(parallelly.cgroups2.cpuset.cpus.effective = 0:3)
34+
message("getCGroups2CpuSet('cpuset.cpus.effective'): ", parallelly:::getCGroups2CpuSet("cpuset.cpus.effective"))
35+
options(parallelly.cgroups2.cpuset.cpus.effective = NULL)
36+
2837
options(parallelly.cgroups2.cpu.max = 100000L)
2938
message("getCGroups2CpuMax(): ", parallelly:::getCGroups2CpuMax())
3039
options(parallelly.cgroups2.cpu.max = NULL)
@@ -121,6 +130,16 @@ stopifnot(
121130
is.na(value) || value > 0
122131
)
123132

133+
message("- getCGroups2CpuSet()")
134+
value <- parallelly:::getCGroups2CpuSet()
135+
cat(sprintf("CPU set: [n=%d] %s\n", length(value), paste(sQuote(value), collapse = ", ")))
136+
stopifnot(length(value) >= 0L, is.integer(value), !any(is.na(value)))
137+
138+
message("- getCGroups2CpuSet('cpuset.cpus.effective')")
139+
value <- parallelly:::getCGroups2CpuSet("cpuset.cpus.effective")
140+
cat(sprintf("CPU set: [n=%d] %s\n", length(value), paste(sQuote(value), collapse = ", ")))
141+
stopifnot(length(value) >= 0L, is.integer(value), !any(is.na(value)))
142+
124143
message("- getCGroups2CpuMax()")
125144
value <- parallelly:::getCGroups2CpuMax()
126145
cat(sprintf("CPU quota (ratio): %g\n", value))

0 commit comments

Comments
 (0)