So, for the continuing saga was began with this post ...
I am trying to create a systemd unit to create a service that will run a user script, one shot only, during the boot/startup process.
Reviewing the output from
systemd-analyze critical-chain
which I show here
The time when unit became active or started is printed after the "@" character.
The time the unit took to start is printed after the "+" character.
graphical.target @2min 12.383s
└─multi-user.target @2min 12.383s
└─netperf.service @2min 12.160s +222ms
└─network-online.target @2min 12.067s
└─NetworkManager-wait-online.service @49.403s +18.636s
└─NetworkManager.service @35.598s +13.785s
└─dbus.service @35.590s
└─basic.target @35.530s
└─sockets.target @35.530s
└─cups.socket @48.920s
└─sysinit.target @31.916s
└─systemd-timesyncd.service @31.712s +203ms
└─systemd-tmpfiles-setup.service @31.504s +182ms
└─systemd-journal-flush.service @5.554s +25.944s
└─systemd-journald.service @5.250s +283ms
└─systemd-journald.socket @5.233s
└─-.mount @5.218s
└─-.slice @5.218s
I conclude that I want my script, to set my CPU governor and frequency, to run before "multi-user.target" (or should I specify "graphical.target" ?) .
Many examples show using the reference "network.target" for the "After=" directive, but the above report doesn't show that in the sequence. So ... my initial stab at the "service unit" definition is as follows (/etc/systemd/system/localcpufreq.service):
[Unit]
Description=Local CPU Frequency Service and Monitor
After=network-online.target
Before=multi-user.target
StartLimitIntervalSec=0
[Service]
User=root
Type=oneshot
RemainAfterExit=no
ExecStart=/bin/bash /DB001_F2/Oasis/bin/HW_Admin__Power_SetFreqCPU.sh
Restart=on-failure
RestartSec=360
[Install]
WantedBy=multi-user.target
A quick glance will reveal a possible "conflict" or "overlap" between the values of
- Before=multi-user.target
and
- WantedBy=multi-user.target
I am not sure if that is a conflict, or whether, to avoid not getting the login, remove the "Install" section from that.
Any opinions?
My "cpupower" script as it stands:
#!/bin/bash
####################################################################################################
###
### Script to make it easier to learn how to control the CPU 'governor' and CPU clock frequencies
###
### Version 5.1
###
####################################################################################################
#23456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+123456789+
dbg=0
test "${1}" = "--debug1" && { dbg=1 ; shift ; }
test "${1}" = "--debug2" && { dbg=2 ; shift ; }
###
### Edit this script to assign preset defaults appropriate for your local Desktop CPU
###
doMode=99 # userspace
doFreq=2 # 1900MHz
doDefault=0
doStatus=0
doCron=0
doService=0
shopt -s extglob
identifyGovernors()
{
test ${dbg} -gt 0 && echo " >>> ENTERING identifyGovernors ..." >&2
#governors=$(echo "userspace performance ondemand conservative powersave schedutil junker" |
governors=$(grep 'available cpufreq governors:' ${tmp} |
sed 's+\:\ +\:+' |
cut -f2 -d":" |
awk -v dbg="${dbg}" 'BEGIN{
split("", plist) ;
plist[1,1]="userspace";
plist[2,1]="performance";
plist[3,1]="ondemand";
plist[4,1]="conservative";
plist[5,1]="powersave";
plist[6,1]="schedutil";
c=6 ;
for( i=1 ; i <= c ; i++ ){
plist[i,2]=0 ;
} ;
if( dbg == 2 ){
for( j=1 ; j <= c ; j++ ){
printf("plist[%s] = %s\n", j, plist[j,1] ) | "cat 1>&2" ;
} ;
} ;
}{
for( k=1 ; k <= NF ; k++ ){
if( dbg == 2 ){ printf("\t $%d = %s\n", k, $k ) | "cat 1>&2" ; } ;
valid=0 ;
for( m=1 ; m <= c ; m++ ){
if( dbg == 2 ){ printf("\t\t plist[%d,1] = %s\n", m, plist[m,1] ) | "cat 1>&2" ; } ;
if( $k == plist[m,1] ){
plist[m,2]=1
valid=1
break ;
} ;
} ;
if( valid == 0 ){
printf("\n NOTE: Reported governor mode \"%s\" is not currently handled by programmed logic.\n", $k ) | "cat 1>&2" ;
} ;
} ;
}END{
for( n=1 ; n <= c ; n++ ){
if( plist[n,2] == 1 ){
printf("%s\n", plist[n,1] ) ;
} ;
} ;
}'
)
}
getGov()
{
test ${dbg} -gt 0 && echo " >>> ENTERING getGov ..." >&2
case ${doMode} in
1 ) governor="performance" ;;
2 ) governor="conservative" ;;
3 ) governor="powersave" ;;
4 ) governor="ondemand" ;;
5 ) governor="schedutil" ;;
98 ) ;; ### governor specified on command line
99 ) governor="userspace" ;;
esac
}
identifyFrequencies()
{
test ${dbg} -gt 0 && echo " >>> ENTERING identifyFrequencies ..." >&2
steps=$(grep 'Pstate-' ${tmp} |
sed 's+\:\ \ +\:+' |
cut -f2 -d":" |
awk '{
n=split($0, vals, ",") ;
for( i=1 ; i <= n ; i++ ){
printf("%s\n", vals[i] ) ;
} ;
}'
)
}
getFreq()
{
test ${dbg} -gt 0 && echo " >>> ENTERING getFreq ..." >&2
case ${doMode} in
[1-5] ) return ;; ### No need to so set frequency for these modes
* ) ;;
esac
fmax=$(echo "${steps}" | head -1 )
fmin=$(echo "${steps}" | tail -1 )
test -n "${fmin}" || fmin=$(echo "${steps}" | tail -2 | head -1 )
fopt=( $(echo ${steps} ) )
len=${#fopt[*]}
test ${dbg} -gt 0 && echo -e "\n Number of clock speeds identified: ${len}" >&2
test ${dbg} -gt 1 && echo "doFreq = ${doFreq}" >&2
case ${doFreq} in
#(!+[0-9]) ) frequency="NULL" ;;
97 )
case "${frequency}" in
+([0-9])"MHz" ) ;;
#[0-9]"."+([0-9])"GHz" | [0-9]"."[0-9][0-9]"GHz" )
[0-9]"."+([0-9])"GHz" )
fval=$(echo "${frequency}" | sed 's+GHz++' )
#fval=$(echo "scale=0 ; 1000 * ${fval}" | bc ) ### This form doesn't work
#fval=$(echo "1000 * ${fval}" | bc | cut -f1 -d\. ) ### Kludgy fix
fval=$(echo "1000 * ${fval}" / 1 | bc ) ### Elegant fix
frequency="${fval}MHz"
;;
esac
return ;;
98 ) frequency="${fmin}" ; return ;;
99 ) frequency="${fmax}" ; return ;;
+([0-9]) )
test ${doFreq} -gt ${len} && { printf "\n\t ERROR: User has specified frequncy index which is out of range. Max positional choices = 4.\n Bye!\n\n" ; exit 1 ; }
frequency="${fopt[$(expr ${doFreq} - 1)]}" ; return ;;
#'([:alpha:])' )
* ) printf "\n\t ERROR: User has specified frequency index which is invalid. Re-run with no options to obtain report of choices available.\n Bye!\n\n" ; exit 1 ;;
esac
}
reportOptions()
{
test ${dbg} -gt 0 && echo " >>> ENTERING reportOptions ..." >&2
printf "\n Choices Available for 'CPU Frequency Governor' labels:\n\n"
echo "${governors}" | awk '{ printf("\t %s\n", $0 ); }'
printf "\n Choices Available for 'userspace' fixed CPU 'frequency':\n\n"
echo "${steps}" | awk '{ printf("\t %7s\n", $0 ); }'
printf "\n Syntax: $(basename $0 )\n\
\t\t[\t--list |\n\
\t\t\t--detail |\n\
\t\t\t--default |\n\
\t\t\t[ --mode {governor_label} |\n\
\t\t\t --max |\n\
\t\t\t --min |\n\
\t\t\t --laptop |\n\
\t\t\t --load |\n\
\t\t\t --adaptive ]\n\
\t\t\t[ --freq {frequency} ]\n\
\t\t]\n\n"
printf "\n View detailed report generated by 'cpupower' ? [y/N] => " ; read ans ; test -n "$ans" || ans="N"
case "${ans}" in
y* | Y* )
printf "\n Contents of raw report from 'cpupower' (${tmp}):\n\n"
awk '{ printf("\t|%s\n", $0 ) ; }' <"${tmp}"
echo ""
ls -l "${tmp}"
echo ""
rm -i "${tmp}"
;;
* ) rm -f "${tmp}" ;;
esac
}
setGovernor()
{
test ${dbg} -gt 0 && echo " >>> ENTERING setGovernor ..." >&2
test -n "${governor}" || { printf "\n ERROR: No 'governor' label specified. Unable to proceed.\n\n" ; exit 1 ; }
#testor=$(grep 'available cpufreq governors:' ${tmp} | grep "${governor}" )
testor=$(echo "${governors}" | grep "${governor}" )
test -n "${testor}" || { printf "\n ERROR: Governor '${governor}' is available for your hardware. Unable to proceed.\n\n" ; exit 1 ; }
### Set CPU frequency under USER-defined control
COM="${command} --cpu 'all' frequency-set --governor '${governor}'"
printf "\n COMMAND: ${COM}\n"
eval ${COM}
}
setFrequency()
{
test ${dbg} -gt 0 && echo " >>> ENTERING setFrequency ..." >&2
test -n "${frequency}" || { printf "\n ERROR: No CPU frequency value specified. Unable to proceed.\n\n" ; exit 1 ; }
testor=$(grep 'Pstate-' ${tmp} | grep "${frequency}" )
test -n "${testor}" || { printf "\n ERROR: Frequency '${frequency}' is not available for your hardware. Unable to proceed.\n\n" ; exit 1 ; }
### Can ONLY set frequency by itself; no other parameters allowed
COM="${command} --cpu 'all' frequency-set --freq '${frequency}'"
printf "\n COMMAND: ${COM}\n"
eval ${COM}
}
reportStatus()
{
now=$( date '+%Y-%m-%d %H:%M:%S' )
for cpu in 0 1 2 3
do
fmax=$(cat /sys/devices/system/cpu/cpu${cpu}/cpufreq/scaling_max_freq )
fmin=$(cat /sys/devices/system/cpu/cpu${cpu}/cpufreq/scaling_min_freq )
govr=$(cat /sys/devices/system/cpu/cpu${cpu}/cpufreq/scaling_governor )
freq=$(cat /sys/devices/system/cpu/cpu${cpu}/cpufreq/scaling_cur_freq )
printf "${now}| CPU ${cpu}: %s %4s MHz [%s <-> %s]\n" ${govr} $( expr ${freq} / 1000 ) $( expr ${fmin} / 1000 ) $( expr ${fmax} / 1000 )
done
}
command=$(which "cpupower" )
test -n "${command}" || { printf "\n ERROR: Unable to locate command 'cpupower'. Unable to proceed.\n\n" ; exit 1 ; }
tmp=/tmp/$(basename "$0" ".sh" ).report
### get details report to parse for available governor labels and CPU frequencies
${command} frequency-info --debug >${tmp}
test -s "${tmp}" || { printf "\n ERROR: ${command} did not generate the required details report. Unable to proceed.\n\n" ; exit 1 ; }
identifyGovernors
identifyFrequencies
test ${dbg} -gt 0 && echo " >>> START parsing ..." >&2
### always report safely and informatively, if no parameters provided
if [ $# -eq 0 ]
then
set - '--list'
fi
while [ $# -gt 0 ]
do
case "${1}" in
"--list" )
reportOptions ; echo "" ; exit 0 ; ;;
"--detail" )
${command} frequency-info --debug ; echo "" ; exit 0 ;;
"--mode" )
doMode=98 ;
governor="${2}" ; shift ; shift ;;
"--max" )
doMode=1 ; shift ;;
"--min" )
doMode=2 ; shift ;;
"--laptop" )
doMode=3 ; shift ;;
"--load" )
doMode=4 ; shift ;;
"--adaptive" )
doMode=5 ; shift ;;
"--freq" )
doMode=99 ;
doFreq=97 ;
frequency="${2}" ; shift ; shift ;;
"--fmax" )
doFreq=99 ; shift ;;
"--fmin" )
doFreq=98 ; shift ;;
"--f"+([0-9]) )
doFreq=$(echo "$1" | cut -c4- ) ; shift ;;
"--default" )
doDefault=1 ; shift ;;
"--status" )
doStatus=1 ; shift ;;
"--cron" )
doCron=1 ; shift ;;
"--service" )
doService=1 ; shift ;;
* )
echo "ERROR: Invalid argument '${1}' on command line." ; exit 1 ;;
esac
done
test ${dbg} -gt 0 && echo " >>> END parsing ..." >&2
if [ ${doService} -eq 1 ]
then
rm -f /var/log/cpufreq.log
fi
if [ ${doStatus} -eq 1 ]
then
if [ ${doCron} -eq 1 ]
then
{ reportStatus ; echo "" ; } >>/var/log/cpufreq.log
else
reportStatus
fi
exit
fi
getGov
getFreq
test ${dbg} -gt 1 && echo frequency = ${frequency} >&2
test ${doDefault} -eq 1 \
&& { printf "\n Will use hard-coded presets:\n\t governor = '${governor}'\n\t frequency = '${frequency}'\n\n" ; } \
|| { printf "\n Will use selected governor: '${governor}' ...\n" ; }
setGovernor
if [ "${governor}" = "userspace" ]
then
test ${doDefault} -eq 0 \
&& { printf "\n Will use selected frequency: '${frequency}' ...\n" ; }
setFrequency
fi
echo ""
rm -i ${tmp}