blob: c961d6bb53e17e75e5f4480f84753269fdbac52a [file]
#!/usr/bin/env expect
############################################################################
# Purpose: Test of --cpu-freq options
############################################################################
# Copyright (C) 2014 Bull S. A. S.
# Bull, Rue Jean Jaures, B.P.68, 78340, Les Clayes-sous-Bois.
# Written by Rod Schultz <Rod.Schultz@bull.com>
#
# This file is part of Slurm, a resource management program.
# For details, see <https://slurm.schedmd.com/>.
# Please also read the included file: DISCLAIMER.
#
# Slurm is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with Slurm; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
############################################################################
source ./globals
# Test various --cpu-freq options
# Assumptions
# - the first node selected has frequency scaling, with ondemand and userspace
# - all cpus on a node have the same scaling options.
# - task affinity either cgroups or cpusets
set nerr 0
set test_prog "$test_name.prog"
set avail_freq [split "1000 2000" " "]
set nfreq 0
set have_on_demand 1
set have_user_space 1
set file_out "$test_dir/output"
set wd [pwd]
################################################################
# Run a test case.
# "test_case" is a wrapper for "test_freq" due to current Linux
# not reliably changing frequency, so we can repeat a test
#
# Parameters
# - opt value of --cpu-freq
# - expmin expected value of scaling_min (if 0, don't check)
# - expcur expected value of scaling_cur (if 0, don't check)
# - expmax expected value of scaling_max (if 0, don't check)
# - expgov expected value of scaling_governor (if 0, don't check)
#
# Returns
# 0 on success, 1 on failure
# 2 if measured frequency differs too much from target frequency
# ERROR message is sent to user on failure.
################################################################
proc test_freq {opt expmin expcur expmax expgov} {
global bin_rm srun sbatch test_node test_cpu wd number file_out test_name
log_user 0
exec $bin_rm -f $file_out
set job_id 0
spawn $sbatch -w $test_node --exclusive -o $file_out -t1 $test_name.batch $test_cpu $opt $wd $srun
expect {
-re "Submitted batch job ($number)" {
set job_id $expect_out(1,string)
exp_continue
}
eof {
wait
}
}
if {$job_id == 0} {
fail "Batch submit failure"
}
#
# Wait for job to complete
#
wait_for_job -fail $job_id "DONE"
wait_for_file -fail $file_out
set line ""
set start_value ""
set test_value ""
set end_value ""
set fd 0
set fd [open $file_out "r"]
# Search for starting values
while {$fd > 0 && [gets $fd line] != -1} {
set pos [string first "scaling_values:" $line]
if {$pos == 0} {
set start_value [string trim $line "\r"]
set start_value [string range $start_value 16 end]
break
}
}
if {$start_value eq ""} {
log_error "--cpu-freq=$opt -- can't find starting values"
close $fd
log_user 1
return 1
}
# Search for test values
while {$fd > 0 && [gets $fd line] != -1} {
set pos [string first "scaling_values:" $line]
if {$pos == 0} {
set test_value [string trim $line "\r"]
set test_value [string range $test_value 16 end]
break
}
}
if {$test_value eq ""} {
log_error "--cpu-freq=$opt -- can't find test values"
close $fd
log_user 1
return 1
}
# Search for ending values
while {$fd > 0 && [gets $fd line] != -1} {
set pos [string first "scaling_values:" $line]
if {$pos == 0} {
set end_value [string trim $line "\r"]
set end_value [string range $end_value 16 end]
break
}
}
if {$end_value eq ""} {
log_error "--cpu-freq=$opt -- can't find ending values"
close $fd
log_user 1
return 1
}
close $fd
log_debug "--cpu-freq=$opt"
log_debug "Start_freq: $start_value"
log_debug "Test_freq: $test_value"
log_debug "End_freq: $end_value"
set strt_vals [split $start_value " "]
set sgov [string range [lindex $strt_vals 0] 4 end]
set smin [string range [lindex $strt_vals 1] 4 end]
set scur [string range [lindex $strt_vals 2] 4 end]
set smax [string range [lindex $strt_vals 3] 4 end]
set tst_vals [split $test_value " "]
set tgov [string range [lindex $tst_vals 0] 4 end]
set tmin [string range [lindex $tst_vals 1] 4 end]
set tcur [string range [lindex $tst_vals 2] 4 end]
set tmax [string range [lindex $tst_vals 3] 4 end]
set end_vals [split $end_value " "]
set egov [string range [lindex $end_vals 0] 4 end]
set emin [string range [lindex $end_vals 1] 4 end]
set ecur [string range [lindex $end_vals 2] 4 end]
set emax [string range [lindex $end_vals 3] 4 end]
if {$expgov != 0 && $tgov ne $expgov} {
log_error "--cpu-freq=$opt -- test governor $tgov not expected ($expgov)"
log_user 1
return 1
}
if {$expmin != 0 && $tmin ne $expmin} {
log_error "--cpu-freq=$opt -- test min $tmin not expected ($expmin)"
log_user 1
return 1
}
# Check that actual frequency is within 10 percent of target
if {$expcur != 0} {
set percent_error [expr abs($tcur - $expcur) * 100 / $expcur]
} else {
set percent_error 0
}
if {$percent_error > 10} {
set real_error 1
if {$opt eq "High"} {
if {$tcur > $expcur} {
log_warn "--cpu-freq=$opt -- test cur $tcur higher than ($expcur). This is expected as scaling_cur_freq is a kernel estimation."
set real_error 0
}
}
if {$real_error} {
log_warn "--cpu-freq=$opt -- test cur $tcur not expected ($expcur, error is $percent_error percent)"
log_user 1
return 2
}
} elseif {$expcur != 0} {
log_debug "Difference between requested and actual frequency is $percent_error percent ($tcur vs. $expcur)"
}
if {$expmax != 0 && $tmax ne $expmax} {
log_error "--cpu-freq=$opt -- test max $tmax not expected ($expmax)"
log_user 1
return 1
}
if {$sgov ne $egov} {
log_error "--cpu-freq=$opt -- starting governor $sgov not reset ($egov)"
log_user 1
return 1
}
if {$smin ne $emin} {
log_error "--cpu-freq=$opt -- starting min $smin not reset ($emin)"
log_user 1
return 1
}
if {$smax ne $emax} {
log_error "--cpu-freq=$opt -- starting max $smax not reset ($emax)"
log_user 1
return 1
}
log_user 1
return 0
}
proc test_case {opt expmin expcur expmax expgov} {
set retry_cnt 5
for {set inx 0} {$inx < $retry_cnt} {incr inx} {
set rc [test_freq $opt $expmin $expcur $expmax $expgov]
if {$rc != 2} {
return $rc
}
}
log_error "--cpu-freq control failing repeatedly"
return $rc
}
if {[is_running_in_container]} {
skip "This test will not work in a container"
}
# Check environment
if {![param_contains [get_affinity_types] "affinity"]} {
skip "This test requires some form of task affinity"
}
if {[param_contains [get_config_param "SlurmdParameters"] "config_overrides"]} {
log_warn "SlurmdParameters=config_overrides is discouraged. Proceeding assuming that the number of CPUs declared is accurate."
}
log_user 0
spawn $scontrol show config
expect {
-re "CpuFreqGovernors *= ($re_word_str)" {
if {[string first "OnDemand" $expect_out(1,string)] == -1} {
set have_on_demand 0
}
if {[string first "UserSpace" $expect_out(1,string)] == -1} {
set have_user_space 0
}
exp_continue
}
eof {
wait
}
}
# Identify a node to use
set timeout $max_job_delay
set test_node 0
spawn $srun --exclusive -n1 -t1 env
expect {
-re "SLURMD_NODENAME=($re_word_str)" {
set test_node $expect_out(1,string)
exp_continue
}
eof {
wait
}
}
if {$test_node == 0} {
fail "Failed to get hostname"
}
# Determine CPU count on that node.
# We will assume the tests will run on the highest numbered CPU
set test_cpu -1
spawn $scontrol show node $test_node
expect {
-re "CPUTot=($number)" {
set test_cpu $expect_out(1,string)
exp_continue
}
eof {
wait
}
}
log_user 1
if {$test_cpu == -1} {
fail "Failed to get CPU count on node ($test_node)"
}
incr test_cpu -1
log_debug "Test node is $test_node, test_cpu is $test_cpu"
# Verify that selected CPU on selected node supports cpu_frequency
set have_on_demand 0
set have_user_space 0
set nfreq 0
spawn $srun -w $test_node --exclusive -t1 ./$test_name.bash $test_cpu
expect {
-re "scaling frequency not supported" {
skip "Test requires frequency scaling"
}
-re "available_governors (\[a-zA-Z \]+)" {
if {[string first "ondemand" $expect_out(1,string)] >= 0} {
set have_on_demand 1
}
if {[string first "userspace" $expect_out(1,string)] >= 0} {
set have_user_space 1
}
exp_continue
}
-re "available_frequencies (\[0-9 \]+)" {
set tmp [string trimright $expect_out(1,string)]
set freq_line [string trimright $tmp]
set freqs [split $freq_line " "]
set avail_freq [lsort -integer $freqs]
set nfreq [llength $avail_freq]
exp_continue
}
eof {
wait
}
}
if {$nfreq < 2} {
skip "Test requires at least 2 available frequencies"
}
if {$have_user_space == 0} {
skip "Test requires UserSpace governor"
}
if {$have_on_demand == 0} {
skip "Test requires OnDemand governor"
}
set xmx [expr ($nfreq - 1) / 2]
set xlow [lindex $avail_freq 0]
set xhigh [lindex $avail_freq $nfreq-1]
set xhighm1 [lindex $avail_freq $nfreq-2]
set xmed [lindex $avail_freq $xmx]
log_debug "Frequencies: low:$xlow medium:$xmed highm1:$xhighm1 high:$xhigh"
#
# Now run the tests
#
incr nerr [test_case userspace 0 0 0 userspace]
if {$have_on_demand == 1} {
incr nerr [test_case ondemand 0 0 0 ondemand]
}
incr nerr [test_case Low $xlow $xlow 0 userspace]
incr nerr [test_case High 0 $xhigh $xhigh userspace]
incr nerr [test_case HighM1 0 $xhighm1 0 userspace]
incr nerr [test_case Medium 0 $xmed 0 userspace]
incr nerr [test_case $xmed 0 $xmed 0 userspace]
if {$nfreq > 2} {
set mxx [expr $nfreq - 1]
set minfrq [lindex $avail_freq 1]
set maxfrq [lindex $avail_freq $mxx]
set opt "$minfrq-$maxfrq:ondemand"
incr nerr [test_case $opt $minfrq 0 $maxfrq ondemand]
}
if {$nerr != 0} {
fail "$nerr test cases failed."
}