@@ -27,7 +27,10 @@ use starnix_uapi::{errno, error, pid_t};
27
27
use std:: collections:: btree_map:: Entry ;
28
28
use std:: collections:: BTreeMap ;
29
29
use std:: ops:: Deref ;
30
- use std:: sync:: { Arc , Weak } ;
30
+ use std:: sync:: { Arc , OnceLock , Weak } ;
31
+
32
+ const CONTROLLERS_FILE : & str = "cgroup.controllers" ;
33
+ const PROCS_FILE : & str = "cgroup.procs" ;
31
34
32
35
/// Common operations of all cgroups.
33
36
pub trait CgroupOps : Send + Sync + ' static {
@@ -47,12 +50,12 @@ pub trait CgroupOps: Send + Sync + 'static {
47
50
/// child with `name` is not found.
48
51
fn remove_child ( & self , name : & FsStr ) -> Result < CgroupHandle , Errno > ;
49
52
50
- /// Return a `VecDirectoryEntry` for each child of the cgroup .
51
- fn get_directory_entries ( & self ) -> Vec < VecDirectoryEntry > ;
53
+ /// Return a `VecDirectoryEntry` for each interface file and each child .
54
+ fn get_entries ( & self ) -> Vec < VecDirectoryEntry > ;
52
55
53
- /// Find a child with the given name and return its `node`, if exists. Errors if a child with
54
- /// `name` is not found.
55
- fn get_child_node ( & self , name : & FsStr ) -> Result < FsNodeHandle , Errno > ;
56
+ /// Find a child or interface file with the given name and return its `node`, if exists. Errors
57
+ /// if such a node was not found.
58
+ fn get_node ( & self , name : & FsStr ) -> Result < FsNodeHandle , Errno > ;
56
59
57
60
/// Return all pids that belong to this cgroup.
58
61
fn get_pids ( & self ) -> Vec < pid_t > ;
@@ -73,20 +76,46 @@ pub trait CgroupOps: Send + Sync + 'static {
73
76
pub struct CgroupRoot {
74
77
/// Sub-cgroups of this cgroup.
75
78
children : Mutex < CgroupChildren > ,
79
+
80
+ /// Interface nodes of the root cgroup. Lazily by `init()` and immutable after.
81
+ interface_nodes : OnceLock < BTreeMap < FsString , FsNodeHandle > > ,
76
82
}
77
83
impl CgroupRoot {
84
+ /// Since `CgroupRoot` is part of the `FileSystem` (see `CgroupFsV1::new_fs` and
85
+ /// `CgroupFsV2::new_fs`), initializing a `CgroupRoot` has two steps:
86
+ ///
87
+ /// - new() to create a `FileSystem`,
88
+ /// - init() to use the newly created `FileSystem` to create the `FsNode`s of the `CgroupRoot`.
78
89
pub fn new ( ) -> Arc < CgroupRoot > {
79
90
Arc :: new ( Self :: default ( ) )
80
91
}
81
92
82
- /// Since the `FileSystem` owns the `FsNode` of the root node, create the `FsNodeOps` so that
83
- /// the `FileSystem` can create the `FsNode` of the root.
84
- pub fn create_node_ops (
85
- self : Arc < Self > ,
86
- current_task : & CurrentTask ,
87
- fs : & FileSystemHandle ,
88
- ) -> CgroupDirectoryHandle {
89
- CgroupDirectory :: new ( current_task, fs, Arc :: downgrade ( & ( self as Arc < dyn CgroupOps > ) ) )
93
+ /// Populate `interface_nodes` with nodes of the cgroup root directory, then set
94
+ /// `CgroupDirectoryHandle` to be the root node of the `FileSystem`. Can only be called once.
95
+ pub fn init ( self : & Arc < Self > , current_task : & CurrentTask , fs : & FileSystemHandle ) {
96
+ let cloned = self . clone ( ) ;
97
+ let weak_ops = Arc :: downgrade ( & ( cloned as Arc < dyn CgroupOps > ) ) ;
98
+ self . interface_nodes
99
+ . set ( BTreeMap :: from ( [
100
+ (
101
+ PROCS_FILE . into ( ) ,
102
+ fs. create_node (
103
+ current_task,
104
+ ControlGroupNode :: new ( weak_ops. clone ( ) ) ,
105
+ FsNodeInfo :: new_factory ( mode ! ( IFREG , 0o644 ) , FsCred :: root ( ) ) ,
106
+ ) ,
107
+ ) ,
108
+ (
109
+ CONTROLLERS_FILE . into ( ) ,
110
+ fs. create_node (
111
+ current_task,
112
+ BytesFile :: new_node ( b"" . to_vec ( ) ) ,
113
+ FsNodeInfo :: new_factory ( mode ! ( IFREG , 0o444 ) , FsCred :: root ( ) ) ,
114
+ ) ,
115
+ ) ,
116
+ ] ) )
117
+ . expect ( "CgroupRoot is only initialized once" ) ;
118
+ fs. set_root ( CgroupDirectory :: new ( weak_ops) ) ;
90
119
}
91
120
}
92
121
@@ -111,14 +140,26 @@ impl CgroupOps for CgroupRoot {
111
140
children. remove_child ( name)
112
141
}
113
142
114
- fn get_directory_entries ( & self ) -> Vec < VecDirectoryEntry > {
143
+ fn get_entries ( & self ) -> Vec < VecDirectoryEntry > {
144
+ let entries = self . interface_nodes . get ( ) . expect ( "CgroupRoot is initialized" ) . iter ( ) . map (
145
+ |( name, child) | VecDirectoryEntry {
146
+ entry_type : DirectoryEntryType :: REG ,
147
+ name : name. clone ( ) ,
148
+ inode : Some ( child. info ( ) . ino ) ,
149
+ } ,
150
+ ) ;
115
151
let children = self . children . lock ( ) ;
116
- children. get_directory_entries ( )
152
+ entries . chain ( children. get_entries ( ) ) . collect ( )
117
153
}
118
154
119
- fn get_child_node ( & self , name : & FsStr ) -> Result < FsNodeHandle , Errno > {
120
- let children = self . children . lock ( ) ;
121
- children. get_node ( name)
155
+ fn get_node ( & self , name : & FsStr ) -> Result < FsNodeHandle , Errno > {
156
+ if let Some ( node) = self . interface_nodes . get ( ) . expect ( "CgroupRoot is initialized" ) . get ( name)
157
+ {
158
+ Ok ( node. clone ( ) )
159
+ } else {
160
+ let children = self . children . lock ( ) ;
161
+ children. get_node ( name)
162
+ }
122
163
}
123
164
124
165
fn get_pids ( & self ) -> Vec < pid_t > {
@@ -160,19 +201,16 @@ impl CgroupChildren {
160
201
Ok ( child_entry. remove ( ) )
161
202
}
162
203
163
- fn get_directory_entries ( & self ) -> Vec < VecDirectoryEntry > {
164
- self . 0
165
- . iter ( )
166
- . map ( |( name, child) | VecDirectoryEntry {
167
- entry_type : DirectoryEntryType :: DIR ,
168
- name : name. clone ( ) ,
169
- inode : Some ( child. node . info ( ) . ino ) ,
170
- } )
171
- . collect ( )
204
+ fn get_entries ( & self ) -> impl IntoIterator < Item = VecDirectoryEntry > + ' _ {
205
+ self . 0 . iter ( ) . map ( |( name, child) | VecDirectoryEntry {
206
+ entry_type : DirectoryEntryType :: DIR ,
207
+ name : name. clone ( ) ,
208
+ inode : Some ( child. directory_node . info ( ) . ino ) ,
209
+ } )
172
210
}
173
211
174
212
fn get_node ( & self , name : & FsStr ) -> Result < FsNodeHandle , Errno > {
175
- self . 0 . get ( name) . map ( |child| child. node . clone ( ) ) . ok_or_else ( || errno ! ( ENOENT ) )
213
+ self . 0 . get ( name) . map ( |child| child. directory_node . clone ( ) ) . ok_or_else ( || errno ! ( ENOENT ) )
176
214
}
177
215
}
178
216
@@ -201,19 +239,43 @@ pub struct Cgroup {
201
239
state : Mutex < CgroupState > ,
202
240
203
241
/// The directory node associated with this control group.
204
- node : FsNodeHandle ,
242
+ directory_node : FsNodeHandle ,
243
+
244
+ /// The interface nodes associated with this control group.
245
+ interface_nodes : BTreeMap < FsString , FsNodeHandle > ,
205
246
}
206
247
pub type CgroupHandle = Arc < Cgroup > ;
207
248
208
249
impl Cgroup {
209
250
pub fn new ( current_task : & CurrentTask , fs : & FileSystemHandle ) -> CgroupHandle {
210
251
Arc :: new_cyclic ( |weak| {
211
- let node = fs. create_node (
212
- current_task,
213
- CgroupDirectory :: new ( current_task, fs, weak. clone ( ) as Weak < dyn CgroupOps > ) ,
214
- FsNodeInfo :: new_factory ( mode ! ( IFDIR , 0o755 ) , FsCred :: root ( ) ) ,
215
- ) ;
216
- Self { state : Default :: default ( ) , node }
252
+ let weak_ops = weak. clone ( ) as Weak < dyn CgroupOps > ;
253
+ Self {
254
+ state : Default :: default ( ) ,
255
+ directory_node : fs. create_node (
256
+ current_task,
257
+ CgroupDirectory :: new ( weak_ops. clone ( ) ) ,
258
+ FsNodeInfo :: new_factory ( mode ! ( IFDIR , 0o755 ) , FsCred :: root ( ) ) ,
259
+ ) ,
260
+ interface_nodes : BTreeMap :: from ( [
261
+ (
262
+ PROCS_FILE . into ( ) ,
263
+ fs. create_node (
264
+ current_task,
265
+ ControlGroupNode :: new ( weak_ops. clone ( ) ) ,
266
+ FsNodeInfo :: new_factory ( mode ! ( IFREG , 0o644 ) , FsCred :: root ( ) ) ,
267
+ ) ,
268
+ ) ,
269
+ (
270
+ CONTROLLERS_FILE . into ( ) ,
271
+ fs. create_node (
272
+ current_task,
273
+ BytesFile :: new_node ( b"" . to_vec ( ) ) ,
274
+ FsNodeInfo :: new_factory ( mode ! ( IFREG , 0o444 ) , FsCred :: root ( ) ) ,
275
+ ) ,
276
+ ) ,
277
+ ] ) ,
278
+ }
217
279
} )
218
280
}
219
281
}
@@ -249,14 +311,23 @@ impl CgroupOps for Cgroup {
249
311
state. children . remove_child ( name)
250
312
}
251
313
252
- fn get_directory_entries ( & self ) -> Vec < VecDirectoryEntry > {
314
+ fn get_entries ( & self ) -> Vec < VecDirectoryEntry > {
315
+ let entries = self . interface_nodes . iter ( ) . map ( |( name, child) | VecDirectoryEntry {
316
+ entry_type : DirectoryEntryType :: REG ,
317
+ name : name. clone ( ) ,
318
+ inode : Some ( child. info ( ) . ino ) ,
319
+ } ) ;
253
320
let state = self . state . lock ( ) ;
254
- state. children . get_directory_entries ( )
321
+ entries . chain ( state. children . get_entries ( ) ) . collect ( )
255
322
}
256
323
257
- fn get_child_node ( & self , name : & FsStr ) -> Result < FsNodeHandle , Errno > {
258
- let state = self . state . lock ( ) ;
259
- state. children . get_node ( name)
324
+ fn get_node ( & self , name : & FsStr ) -> Result < FsNodeHandle , Errno > {
325
+ if let Some ( node) = self . interface_nodes . get ( name) {
326
+ Ok ( node. clone ( ) )
327
+ } else {
328
+ let state = self . state . lock ( ) ;
329
+ state. children . get_node ( name)
330
+ }
260
331
}
261
332
262
333
fn get_pids ( & self ) -> Vec < pid_t > {
@@ -280,33 +351,11 @@ impl CgroupOps for Cgroup {
280
351
pub struct CgroupDirectory {
281
352
/// The associated cgroup.
282
353
cgroup : Weak < dyn CgroupOps > ,
283
-
284
- /// Node that backs `cgroup.procs`
285
- procs_node : FsNodeHandle ,
286
-
287
- /// Node that backs `cgroup.controllers`
288
- controllers_node : FsNodeHandle ,
289
354
}
290
355
291
356
impl CgroupDirectory {
292
- pub fn new (
293
- current_task : & CurrentTask ,
294
- fs : & FileSystemHandle ,
295
- cgroup : Weak < dyn CgroupOps > ,
296
- ) -> CgroupDirectoryHandle {
297
- let procs_node = fs. create_node (
298
- current_task,
299
- ControlGroupNode :: new ( cgroup. clone ( ) ) ,
300
- FsNodeInfo :: new_factory ( mode ! ( IFREG , 0o644 ) , FsCred :: root ( ) ) ,
301
- ) ;
302
-
303
- let controllers_node = fs. create_node (
304
- current_task,
305
- BytesFile :: new_node ( b"" . to_vec ( ) ) ,
306
- FsNodeInfo :: new_factory ( mode ! ( IFREG , 0o444 ) , FsCred :: root ( ) ) ,
307
- ) ;
308
-
309
- CgroupDirectoryHandle ( Arc :: new ( Self { cgroup, procs_node, controllers_node } ) )
357
+ pub fn new ( cgroup : Weak < dyn CgroupOps > ) -> CgroupDirectoryHandle {
358
+ CgroupDirectoryHandle ( Arc :: new ( Self { cgroup } ) )
310
359
}
311
360
}
312
361
@@ -335,22 +384,7 @@ impl FsNodeOps for CgroupDirectoryHandle {
335
384
_current_task : & CurrentTask ,
336
385
_flags : OpenFlags ,
337
386
) -> Result < Box < dyn FileOps > , Errno > {
338
- let mut entries = vec ! [
339
- VecDirectoryEntry {
340
- entry_type: DirectoryEntryType :: REG ,
341
- name: FsString :: from( "cgroup.procs" ) ,
342
- inode: Some ( self . procs_node. info( ) . ino) ,
343
- } ,
344
- VecDirectoryEntry {
345
- entry_type: DirectoryEntryType :: REG ,
346
- name: FsString :: from( "cgroup.controllers" ) ,
347
- inode: Some ( self . controllers_node. info( ) . ino) ,
348
- } ,
349
- ] ;
350
-
351
- entries. extend ( self . cgroup ( ) ?. get_directory_entries ( ) ) ;
352
-
353
- Ok ( VecDirectory :: new_file ( entries) )
387
+ Ok ( VecDirectory :: new_file ( self . cgroup ( ) ?. get_entries ( ) ) )
354
388
}
355
389
356
390
fn mkdir (
@@ -366,7 +400,7 @@ impl FsNodeOps for CgroupDirectoryHandle {
366
400
node. update_info ( |info| {
367
401
info. link_count += 1 ;
368
402
} ) ;
369
- Ok ( child. node . clone ( ) )
403
+ Ok ( child. directory_node . clone ( ) )
370
404
}
371
405
372
406
fn mknod (
@@ -385,7 +419,7 @@ impl FsNodeOps for CgroupDirectoryHandle {
385
419
fn unlink (
386
420
& self ,
387
421
_locked : & mut Locked < ' _ , FileOpsCore > ,
388
- _node : & FsNode ,
422
+ node : & FsNode ,
389
423
_current_task : & CurrentTask ,
390
424
name : & FsStr ,
391
425
child : & FsNodeHandle ,
@@ -401,6 +435,10 @@ impl FsNodeOps for CgroupDirectoryHandle {
401
435
let removed = cgroup. remove_child ( name) ?;
402
436
assert ! ( Arc :: ptr_eq( & ( removed as Arc <dyn CgroupOps >) , & child_cgroup) ) ;
403
437
438
+ node. update_info ( |info| {
439
+ info. link_count -= 1 ;
440
+ } ) ;
441
+
404
442
Ok ( ( ) )
405
443
}
406
444
@@ -423,12 +461,7 @@ impl FsNodeOps for CgroupDirectoryHandle {
423
461
_current_task : & CurrentTask ,
424
462
name : & FsStr ,
425
463
) -> Result < FsNodeHandle , Errno > {
426
- let cgroup = self . cgroup ( ) ?;
427
- match & * * name {
428
- b"cgroup.controllers" => Ok ( self . controllers_node . clone ( ) ) ,
429
- b"cgroup.procs" => Ok ( self . procs_node . clone ( ) ) ,
430
- _ => cgroup. get_child_node ( name) ,
431
- }
464
+ self . cgroup ( ) ?. get_node ( name)
432
465
}
433
466
}
434
467
0 commit comments