@@ -72,15 +72,16 @@ def disengage!
7272
7373 # Get thread id's of all running processes.
7474 def all_tasks
75- ( CpuSet . read ( :tasks ) || `ps -eo tid=` ) . lines . map ( &:strip ) . map ( &:to_i )
75+ # In cgroup v2 the task list doesn't necessarily include all thread id's.
76+ ( CpuSet . tasks + "\n " + `ps -eo tid=` ) . lines . map ( &:strip ) . map ( &:to_i ) . uniq
7677 end
7778
7879 # Get list of all cpu numbers.
7980 def all_cpus
8081 return @all_cpus if defined? ( @all_cpus )
8182
8283 # Read from root cpuset so our view is not limited by any current cpuset.
83- @all_cpus = Helpers . list_to_ints ( CpuSet . read ( : cpus) )
84+ @all_cpus = Helpers . list_to_ints ( CpuSet . cpus )
8485
8586 # If that didn't work just guess at it.
8687 @all_cpus ||= ( 0 ... Etc . nprocessors ) . to_a
@@ -196,6 +197,15 @@ def disable
196197 class CpuSet
197198 include Sudo
198199
200+ # If cgroup v2 is mounted (systemd unified hierarchy) but not managing
201+ # cpusets we can mount them and use them in the old fashion.
202+ # If cgroup v2 is managing cpusets we can integrate with the already mounted fs.
203+ # If cpusets aren't being managed by v2 you can enable it with:
204+ # echo +cpuset | sudo tee /sys/fs/cgroup/cgroup.subtree_control
205+ # (provided you don't have a cpusets mount somewhere else).
206+
207+ CGROUP_V2_ROOT = "/sys/fs/cgroup"
208+
199209 # Man cpuset(7) says "/dev/cpuset" but mounting there may fail when /dev is a devtmpfs.
200210 DEFAULT_ROOT = "/cpusets"
201211
@@ -213,24 +223,64 @@ def self.find_root
213223 end
214224 end
215225
226+ if read ( "cgroup.subtree_control" , CGROUP_V2_ROOT ) &.split ( ' ' ) &.include? ( 'cpuset' )
227+ @cgroup_v2 = true
228+ return @root = CGROUP_V2_ROOT
229+ end
230+
216231 @root = DEFAULT_ROOT . then do |dir |
217232 Sudo . sudo ( "mkdir" , "-p" , dir ) unless File . exist? ( dir )
218233
219234 Sudo . sudo ( "mount" , "-t" , "cpuset" , "none" , dir )
220235
221- # If the fs isn't mounted , return nil.
222- dir if File . exist? ( " #{ dir } / tasks" )
236+ # If the fs failed to mount , return nil.
237+ dir if File . exist? ( self . file_path ( : tasks, dir ) )
223238 end
224239 end
225240
226- def self . read ( key )
227- path = "#{ find_root } /#{ key } "
241+ V2_PATHS = {
242+ cpu_exclusive : "cpuset.cpus.exclusive" ,
243+ cpus : "cpuset.cpus" ,
244+ effective_cpus : "cpuset.cpus.effective" ,
245+ effective_mems : "cpuset.mems.effective" ,
246+ mems : "cpuset.mems" ,
247+ sched_load_balance : nil , # not available in the v2 fs
248+ tasks : "cgroup.procs" ,
249+ }
250+
251+ def self . file_path ( name , dir = nil )
252+ dir ||= find_root
253+ name = V2_PATHS . fetch ( name ) if cgroup_v2?
254+
255+ return unless name
256+
257+ File . join ( dir , name . to_s )
258+ end
259+
260+ def self . read ( name , dir = nil )
261+ path = file_path ( name , dir )
228262
229- return unless File . exist? ( path )
263+ return unless path && File . exist? ( path )
230264
231265 File . read ( path )
232266 end
233267
268+ def self . cgroup_v2?
269+ @cgroup_v2
270+ end
271+
272+ def self . cpus
273+ read ( :effective_cpus ) &.strip
274+ end
275+
276+ def self . mems
277+ read ( :effective_mems ) &.strip
278+ end
279+
280+ def self . tasks
281+ read ( :tasks )
282+ end
283+
234284 def initialize ( name , cpus : nil , tasks : [ ] , **kwargs )
235285 @name = name
236286 @cpus = cpus
@@ -255,7 +305,7 @@ def create
255305
256306 # Both cpus and mems must be set in order to add tasks.
257307 # Default mems to the root value.
258- @settings [ :mems ] ||= File . read ( " #{ self . class . find_root } / mems" ) . strip
308+ @settings [ :mems ] ||= self . class . mems
259309
260310 # Settings must be written before tasks can be added.
261311 @settings . each_pair do |key , value |
@@ -267,9 +317,12 @@ def create
267317 end
268318
269319 def write_setting ( key , value , verbose : true )
320+ key_path = self . class . file_path ( key , path )
321+ return unless key_path
322+
270323 # If an array is provided (for "tasks") write each item individually.
271324 Array ( value ) . each do |item |
272- write ( " #{ path } / #{ key } " , item , verbose :)
325+ write ( key_path , item , verbose :)
273326 end
274327 end
275328
@@ -278,8 +331,8 @@ def destroy
278331 return unless path && File . directory? ( path )
279332
280333 # Move tasks to parent cpuset as we cannot remove a set that still has tasks.
281- root_tasks = " #{ self . class . find_root } / tasks"
282- File . read ( " #{ path } / tasks" ) . lines . each do |tid |
334+ root_tasks = self . class . file_path ( : tasks)
335+ File . read ( self . class . file_path ( : tasks, path ) ) . lines . each do |tid |
283336 write ( root_tasks , tid , verbose : false )
284337 end
285338
0 commit comments