@@ -19,6 +19,7 @@ use std::sync::Arc;
19
19
use std:: time:: Duration ;
20
20
use std:: time:: Instant ;
21
21
22
+ use :: fb_procfs as procfs;
22
23
use anyhow:: anyhow;
23
24
use anyhow:: bail;
24
25
use anyhow:: Context ;
@@ -157,77 +158,61 @@ fn format_cpumask(cpumask: &[u64], nr_cpus: usize) -> String {
157
158
. fold ( String :: new ( ) , |acc, x| format ! ( "{} {:016X}" , acc, x) )
158
159
}
159
160
160
- // Neither procfs or fb_procfs can determine per-CPU utilization reliably
161
- // with CPU hot[un]plugs. Roll our own.
162
- //
163
- // https://github.com/eminence/procfs/issues/274
164
- // https://github.com/facebookincubator/below/issues/8190
165
- #[ derive( Clone , Debug , Default ) ]
166
- struct MyCpuStat {
167
- user : u64 ,
168
- nice : u64 ,
169
- system : u64 ,
170
- idle : u64 ,
171
- iowait : u64 ,
172
- irq : u64 ,
173
- softirq : u64 ,
174
- steal : u64 ,
161
+ fn read_total_cpu ( reader : & procfs:: ProcReader ) -> Result < procfs:: CpuStat > {
162
+ Ok ( reader
163
+ . read_stat ( )
164
+ . context ( "Failed to read procfs" ) ?
165
+ . total_cpu
166
+ . ok_or_else ( || anyhow ! ( "Could not read total cpu stat in proc" ) ) ?)
175
167
}
176
168
177
- impl MyCpuStat {
178
- fn busy_and_total ( & self ) -> ( u64 , u64 ) {
179
- let busy = self . user + self . system + self . nice + self . irq + self . softirq + self . steal ;
180
- ( busy, self . idle + busy + self . iowait )
181
- }
182
-
183
- fn calc_util ( & self , prev : & MyCpuStat ) -> f64 {
184
- let ( curr_busy, curr_total) = self . busy_and_total ( ) ;
185
- let ( prev_busy, prev_total) = prev. busy_and_total ( ) ;
186
- let busy = curr_busy - prev_busy;
187
- let total = curr_total - prev_total;
188
- if total > 0 {
189
- ( ( busy as f64 ) / ( total as f64 ) ) . clamp ( 0.0 , 1.0 )
190
- } else {
191
- 1.0
192
- }
193
- }
194
- }
195
-
196
- #[ derive( Clone , Debug , Default ) ]
197
- struct MyProcStat {
198
- total : MyCpuStat ,
199
- cpus : BTreeMap < usize , MyCpuStat > ,
200
- }
201
-
202
- impl MyProcStat {
203
- fn read ( ) -> Result < Self > {
204
- let mut result: MyProcStat = Default :: default ( ) ;
205
- for line in std:: fs:: read_to_string ( "/proc/stat" ) ?. lines ( ) {
206
- let mut toks = line. split_whitespace ( ) ;
207
-
208
- let key = toks. next ( ) . ok_or ( anyhow ! ( "no key" ) ) ?;
209
- if !key. starts_with ( "cpu" ) {
210
- break ;
211
- }
212
-
213
- let cputime = MyCpuStat {
214
- user : toks. next ( ) . ok_or ( anyhow ! ( "missing" ) ) ?. parse :: < u64 > ( ) ?,
215
- nice : toks. next ( ) . ok_or ( anyhow ! ( "missing" ) ) ?. parse :: < u64 > ( ) ?,
216
- system : toks. next ( ) . ok_or ( anyhow ! ( "missing" ) ) ?. parse :: < u64 > ( ) ?,
217
- idle : toks. next ( ) . ok_or ( anyhow ! ( "missing" ) ) ?. parse :: < u64 > ( ) ?,
218
- iowait : toks. next ( ) . ok_or ( anyhow ! ( "missing" ) ) ?. parse :: < u64 > ( ) ?,
219
- irq : toks. next ( ) . ok_or ( anyhow ! ( "missing" ) ) ?. parse :: < u64 > ( ) ?,
220
- softirq : toks. next ( ) . ok_or ( anyhow ! ( "missing" ) ) ?. parse :: < u64 > ( ) ?,
221
- steal : toks. next ( ) . ok_or ( anyhow ! ( "missing" ) ) ?. parse :: < u64 > ( ) ?,
222
- } ;
223
-
224
- if key. len ( ) == 3 {
225
- result. total = cputime;
169
+ fn calc_util ( curr : & procfs:: CpuStat , prev : & procfs:: CpuStat ) -> Result < f64 > {
170
+ match ( curr, prev) {
171
+ (
172
+ procfs:: CpuStat {
173
+ user_usec : Some ( prev_user) ,
174
+ nice_usec : Some ( prev_nice) ,
175
+ system_usec : Some ( prev_system) ,
176
+ idle_usec : Some ( prev_idle) ,
177
+ iowait_usec : Some ( prev_iowait) ,
178
+ irq_usec : Some ( prev_irq) ,
179
+ softirq_usec : Some ( prev_softirq) ,
180
+ stolen_usec : Some ( prev_stolen) ,
181
+ ..
182
+ } ,
183
+ procfs:: CpuStat {
184
+ user_usec : Some ( curr_user) ,
185
+ nice_usec : Some ( curr_nice) ,
186
+ system_usec : Some ( curr_system) ,
187
+ idle_usec : Some ( curr_idle) ,
188
+ iowait_usec : Some ( curr_iowait) ,
189
+ irq_usec : Some ( curr_irq) ,
190
+ softirq_usec : Some ( curr_softirq) ,
191
+ stolen_usec : Some ( curr_stolen) ,
192
+ ..
193
+ } ,
194
+ ) => {
195
+ let idle_usec = curr_idle - prev_idle;
196
+ let iowait_usec = curr_iowait - prev_iowait;
197
+ let user_usec = curr_user - prev_user;
198
+ let system_usec = curr_system - prev_system;
199
+ let nice_usec = curr_nice - prev_nice;
200
+ let irq_usec = curr_irq - prev_irq;
201
+ let softirq_usec = curr_softirq - prev_softirq;
202
+ let stolen_usec = curr_stolen - prev_stolen;
203
+
204
+ let busy_usec =
205
+ user_usec + system_usec + nice_usec + irq_usec + softirq_usec + stolen_usec;
206
+ let total_usec = idle_usec + busy_usec + iowait_usec;
207
+ if total_usec > 0 {
208
+ return Ok ( ( ( busy_usec as f64 ) / ( total_usec as f64 ) ) . clamp ( 0.0 , 1.0 ) ) ;
226
209
} else {
227
- result . cpus . insert ( key [ 3 .. ] . parse :: < usize > ( ) ? , cputime ) ;
210
+ return Ok ( 1.0 ) ;
228
211
}
229
212
}
230
- Ok ( result)
213
+ _ => {
214
+ bail ! ( "Missing stats in cpustat" ) ;
215
+ }
231
216
}
232
217
}
233
218
@@ -394,38 +379,50 @@ struct Tuner {
394
379
top : Arc < Topology > ,
395
380
direct_greedy_under : f64 ,
396
381
kick_greedy_under : f64 ,
397
- prev_cpu_stats : BTreeMap < usize , MyCpuStat > ,
382
+ proc_reader : procfs:: ProcReader ,
383
+ prev_cpu_stats : BTreeMap < u32 , procfs:: CpuStat > ,
398
384
dom_utils : Vec < f64 > ,
399
385
}
400
386
401
387
impl Tuner {
402
388
fn new ( top : Arc < Topology > , opts : & Opts ) -> Result < Self > {
389
+ let proc_reader = procfs:: ProcReader :: new ( ) ;
390
+ let prev_cpu_stats = proc_reader
391
+ . read_stat ( ) ?
392
+ . cpus_map
393
+ . ok_or_else ( || anyhow ! ( "Expected cpus_map to exist" ) ) ?;
403
394
Ok ( Self {
404
395
direct_greedy_under : opts. direct_greedy_under / 100.0 ,
405
396
kick_greedy_under : opts. kick_greedy_under / 100.0 ,
406
- prev_cpu_stats : MyProcStat :: read ( ) ?. cpus ,
397
+ proc_reader,
398
+ prev_cpu_stats,
407
399
dom_utils : vec ! [ 0.0 ; top. nr_doms] ,
408
400
top,
409
401
} )
410
402
}
411
403
412
404
fn step ( & mut self , skel : & mut AtroposSkel ) -> Result < ( ) > {
413
- let curr_cpu_stats = MyProcStat :: read ( ) ?. cpus ;
405
+ let curr_cpu_stats = self
406
+ . proc_reader
407
+ . read_stat ( ) ?
408
+ . cpus_map
409
+ . ok_or_else ( || anyhow ! ( "Expected cpus_map to exist" ) ) ?;
414
410
let ti = & mut skel. bss ( ) . tune_input ;
415
411
let mut dom_nr_cpus = vec ! [ 0 ; self . top. nr_doms] ;
416
412
let mut dom_util_sum = vec ! [ 0.0 ; self . top. nr_doms] ;
417
413
418
414
for cpu in 0 ..self . top . nr_cpus {
415
+ let cpu32 = cpu as u32 ;
419
416
// None domain indicates the CPU was offline during
420
- // initialization and None MyCpuStat indicates the CPU has gone
417
+ // initialization and None CpuStat indicates the CPU has gone
421
418
// down since then. Ignore both.
422
419
if let ( Some ( dom) , Some ( curr) , Some ( prev) ) = (
423
420
self . top . cpu_dom [ cpu] ,
424
- curr_cpu_stats. get ( & cpu ) ,
425
- self . prev_cpu_stats . get ( & cpu ) ,
421
+ curr_cpu_stats. get ( & cpu32 ) ,
422
+ self . prev_cpu_stats . get ( & cpu32 ) ,
426
423
) {
427
424
dom_nr_cpus[ dom] += 1 ;
428
- dom_util_sum[ dom] += curr . calc_util ( prev) ;
425
+ dom_util_sum[ dom] += calc_util ( curr , prev) ? ;
429
426
}
430
427
}
431
428
@@ -843,9 +840,10 @@ struct Scheduler<'a> {
843
840
balanced_kworkers : bool ,
844
841
845
842
top : Arc < Topology > ,
843
+ proc_reader : procfs:: ProcReader ,
846
844
847
845
prev_at : Instant ,
848
- prev_total_cpu : MyCpuStat ,
846
+ prev_total_cpu : procfs :: CpuStat ,
849
847
task_loads : BTreeMap < i32 , TaskLoad > ,
850
848
851
849
nr_lb_data_errors : u64 ,
@@ -914,7 +912,8 @@ impl<'a> Scheduler<'a> {
914
912
info ! ( "Atropos Scheduler Attached" ) ;
915
913
916
914
// Other stuff.
917
- let prev_total_cpu = MyProcStat :: read ( ) ?. total ;
915
+ let proc_reader = procfs:: ProcReader :: new ( ) ;
916
+ let prev_total_cpu = read_total_cpu ( & proc_reader) ?;
918
917
919
918
Ok ( Self {
920
919
skel,
@@ -927,6 +926,7 @@ impl<'a> Scheduler<'a> {
927
926
balanced_kworkers : opts. balanced_kworkers ,
928
927
929
928
top : top. clone ( ) ,
929
+ proc_reader,
930
930
931
931
prev_at : Instant :: now ( ) ,
932
932
prev_total_cpu,
@@ -939,8 +939,53 @@ impl<'a> Scheduler<'a> {
939
939
}
940
940
941
941
fn get_cpu_busy ( & mut self ) -> Result < f64 > {
942
- let total_cpu = MyProcStat :: read ( ) ?. total ;
943
- let busy = total_cpu. calc_util ( & self . prev_total_cpu ) ;
942
+ let total_cpu = read_total_cpu ( & mut self . proc_reader ) ?;
943
+ let busy = match ( & self . prev_total_cpu , & total_cpu) {
944
+ (
945
+ procfs:: CpuStat {
946
+ user_usec : Some ( prev_user) ,
947
+ nice_usec : Some ( prev_nice) ,
948
+ system_usec : Some ( prev_system) ,
949
+ idle_usec : Some ( prev_idle) ,
950
+ iowait_usec : Some ( prev_iowait) ,
951
+ irq_usec : Some ( prev_irq) ,
952
+ softirq_usec : Some ( prev_softirq) ,
953
+ stolen_usec : Some ( prev_stolen) ,
954
+ guest_usec : _,
955
+ guest_nice_usec : _,
956
+ } ,
957
+ procfs:: CpuStat {
958
+ user_usec : Some ( curr_user) ,
959
+ nice_usec : Some ( curr_nice) ,
960
+ system_usec : Some ( curr_system) ,
961
+ idle_usec : Some ( curr_idle) ,
962
+ iowait_usec : Some ( curr_iowait) ,
963
+ irq_usec : Some ( curr_irq) ,
964
+ softirq_usec : Some ( curr_softirq) ,
965
+ stolen_usec : Some ( curr_stolen) ,
966
+ guest_usec : _,
967
+ guest_nice_usec : _,
968
+ } ,
969
+ ) => {
970
+ let idle_usec = curr_idle - prev_idle;
971
+ let iowait_usec = curr_iowait - prev_iowait;
972
+ let user_usec = curr_user - prev_user;
973
+ let system_usec = curr_system - prev_system;
974
+ let nice_usec = curr_nice - prev_nice;
975
+ let irq_usec = curr_irq - prev_irq;
976
+ let softirq_usec = curr_softirq - prev_softirq;
977
+ let stolen_usec = curr_stolen - prev_stolen;
978
+
979
+ let busy_usec =
980
+ user_usec + system_usec + nice_usec + irq_usec + softirq_usec + stolen_usec;
981
+ let total_usec = idle_usec + busy_usec + iowait_usec;
982
+ busy_usec as f64 / total_usec as f64
983
+ }
984
+ _ => {
985
+ bail ! ( "Some procfs stats are not populated!" ) ;
986
+ }
987
+ } ;
988
+
944
989
self . prev_total_cpu = total_cpu;
945
990
Ok ( busy)
946
991
}
0 commit comments