Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions mlutils/unix_utils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,28 @@ module Sysconf = struct
external nr_processors_online : unit -> int =
"guestfs_int_mllib_sysconf_nr_processors_online" [@@noalloc]
end

module Cgroup = struct
let v2_cpus () =
let file = "/sys/fs/cgroup/cpu.max" in
if Sys.file_exists file then
try
let line = read_first_line_from_file file in
if String.starts_with ~prefix:"max" line then
None
else
let quota, period =
Scanf.sscanf line "%Ld %Ld" (fun q p -> (q, p)) in
if period > 0L then
Some (max 1 (Int64.to_int (Int64.div quota period)))
else None
with
| Scanf.Scan_failure _ | Failure _ | End_of_file -> None
else
None

let nr_cpus_available () =
match v2_cpus () with
| Some cpus -> cpus
| None -> Sysconf.nr_processors_online ()
end
18 changes: 18 additions & 0 deletions mlutils/unix_utils.mli
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,21 @@ module Sysconf : sig
Note this never fails. In case we cannot get the number of
cores it returns 1. *)
end

module Cgroup : sig
(** Functions to read CPU limits from cgroup filesystems. *)

val v2_cpus : unit -> int option
(** [v2_cpus ()] reads the cgroup v2 CPU quota from
[/sys/fs/cgroup/cpu.max] and returns the number of CPUs
allocated (quota / period), or [None] if the file does not
exist, the quota is "max" (unlimited), or the file cannot
be parsed. The kernel stores quota and period as [long]
values in microseconds, so we parse them as [Int64]. *)

val nr_cpus_available : unit -> int
(** [nr_cpus_available ()] returns the number of CPUs available,
taking into account cgroup v2 CPU limits.
Falls back to {!Sysconf.nr_processors_online} if no cgroup
limits are set. *)
end