forked from Minki/linux
selftest: cpufreq: Add support for cpufreq tests
This patch adds supports for basic cpufreq tests, which can be performed independent of any platform. It does basic tests for now, like - reading all cpufreq files - trying to update them - switching frequencies - switching governors This can be extended to have more specific tests later on. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com>
This commit is contained in:
parent
6320303fb2
commit
e66d5b6737
@ -1,6 +1,7 @@
|
|||||||
TARGETS = bpf
|
TARGETS = bpf
|
||||||
TARGETS += breakpoints
|
TARGETS += breakpoints
|
||||||
TARGETS += capabilities
|
TARGETS += capabilities
|
||||||
|
TARGETS += cpufreq
|
||||||
TARGETS += cpu-hotplug
|
TARGETS += cpu-hotplug
|
||||||
TARGETS += efivarfs
|
TARGETS += efivarfs
|
||||||
TARGETS += exec
|
TARGETS += exec
|
||||||
|
8
tools/testing/selftests/cpufreq/Makefile
Normal file
8
tools/testing/selftests/cpufreq/Makefile
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
all:
|
||||||
|
|
||||||
|
TEST_PROGS := main.sh
|
||||||
|
TEST_FILES := cpu.sh cpufreq.sh governor.sh
|
||||||
|
|
||||||
|
include ../lib.mk
|
||||||
|
|
||||||
|
clean:
|
84
tools/testing/selftests/cpufreq/cpu.sh
Executable file
84
tools/testing/selftests/cpufreq/cpu.sh
Executable file
@ -0,0 +1,84 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# CPU helpers
|
||||||
|
|
||||||
|
# protect against multiple inclusion
|
||||||
|
if [ $FILE_CPU ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
FILE_CPU=DONE
|
||||||
|
fi
|
||||||
|
|
||||||
|
source cpufreq.sh
|
||||||
|
|
||||||
|
for_each_cpu()
|
||||||
|
{
|
||||||
|
cpus=$(ls $CPUROOT | grep "cpu[0-9].*")
|
||||||
|
for cpu in $cpus; do
|
||||||
|
$@ $cpu
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
for_each_non_boot_cpu()
|
||||||
|
{
|
||||||
|
cpus=$(ls $CPUROOT | grep "cpu[1-9].*")
|
||||||
|
for cpu in $cpus; do
|
||||||
|
$@ $cpu
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
#$1: cpu
|
||||||
|
offline_cpu()
|
||||||
|
{
|
||||||
|
printf "Offline $1\n"
|
||||||
|
echo 0 > $CPUROOT/$1/online
|
||||||
|
}
|
||||||
|
|
||||||
|
#$1: cpu
|
||||||
|
online_cpu()
|
||||||
|
{
|
||||||
|
printf "Online $1\n"
|
||||||
|
echo 1 > $CPUROOT/$1/online
|
||||||
|
}
|
||||||
|
|
||||||
|
#$1: cpu
|
||||||
|
reboot_cpu()
|
||||||
|
{
|
||||||
|
offline_cpu $1
|
||||||
|
online_cpu $1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Reboot CPUs
|
||||||
|
# param: number of times we want to run the loop
|
||||||
|
reboot_cpus()
|
||||||
|
{
|
||||||
|
printf "** Test: Running ${FUNCNAME[0]} for $1 loops **\n\n"
|
||||||
|
|
||||||
|
for i in `seq 1 $1`; do
|
||||||
|
for_each_non_boot_cpu offline_cpu
|
||||||
|
for_each_non_boot_cpu online_cpu
|
||||||
|
printf "\n"
|
||||||
|
done
|
||||||
|
|
||||||
|
printf "\n%s\n\n" "------------------------------------------------"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prints warning for all CPUs with missing cpufreq directory
|
||||||
|
print_unmanaged_cpus()
|
||||||
|
{
|
||||||
|
for_each_cpu cpu_should_have_cpufreq_directory
|
||||||
|
}
|
||||||
|
|
||||||
|
# Counts CPUs with cpufreq directories
|
||||||
|
count_cpufreq_managed_cpus()
|
||||||
|
{
|
||||||
|
count=0;
|
||||||
|
|
||||||
|
for cpu in `ls $CPUROOT | grep "cpu[0-9].*"`; do
|
||||||
|
if [ -d $CPUROOT/$cpu/cpufreq ]; then
|
||||||
|
let count=count+1;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo $count;
|
||||||
|
}
|
201
tools/testing/selftests/cpufreq/cpufreq.sh
Executable file
201
tools/testing/selftests/cpufreq/cpufreq.sh
Executable file
@ -0,0 +1,201 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# protect against multiple inclusion
|
||||||
|
if [ $FILE_CPUFREQ ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
FILE_CPUFREQ=DONE
|
||||||
|
fi
|
||||||
|
|
||||||
|
source cpu.sh
|
||||||
|
|
||||||
|
|
||||||
|
# $1: cpu
|
||||||
|
cpu_should_have_cpufreq_directory()
|
||||||
|
{
|
||||||
|
if [ ! -d $CPUROOT/$1/cpufreq ]; then
|
||||||
|
printf "Warning: No cpufreq directory present for $1\n"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_should_not_have_cpufreq_directory()
|
||||||
|
{
|
||||||
|
if [ -d $CPUROOT/$1/cpufreq ]; then
|
||||||
|
printf "Warning: cpufreq directory present for $1\n"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
for_each_policy()
|
||||||
|
{
|
||||||
|
policies=$(ls $CPUFREQROOT| grep "policy[0-9].*")
|
||||||
|
for policy in $policies; do
|
||||||
|
$@ $policy
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
for_each_policy_concurrent()
|
||||||
|
{
|
||||||
|
policies=$(ls $CPUFREQROOT| grep "policy[0-9].*")
|
||||||
|
for policy in $policies; do
|
||||||
|
$@ $policy &
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1: Path
|
||||||
|
read_cpufreq_files_in_dir()
|
||||||
|
{
|
||||||
|
local files=`ls $1`
|
||||||
|
|
||||||
|
printf "Printing directory: $1\n\n"
|
||||||
|
|
||||||
|
for file in $files; do
|
||||||
|
if [ -f $1/$file ]; then
|
||||||
|
printf "$file:"
|
||||||
|
cat $1/$file
|
||||||
|
else
|
||||||
|
printf "\n"
|
||||||
|
read_cpufreq_files_in_dir "$1/$file"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
printf "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
read_all_cpufreq_files()
|
||||||
|
{
|
||||||
|
printf "** Test: Running ${FUNCNAME[0]} **\n\n"
|
||||||
|
|
||||||
|
read_cpufreq_files_in_dir $CPUFREQROOT
|
||||||
|
|
||||||
|
printf "%s\n\n" "------------------------------------------------"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# UPDATE CPUFREQ FILES
|
||||||
|
|
||||||
|
# $1: directory path
|
||||||
|
update_cpufreq_files_in_dir()
|
||||||
|
{
|
||||||
|
local files=`ls $1`
|
||||||
|
|
||||||
|
printf "Updating directory: $1\n\n"
|
||||||
|
|
||||||
|
for file in $files; do
|
||||||
|
if [ -f $1/$file ]; then
|
||||||
|
# file is writable ?
|
||||||
|
local wfile=$(ls -l $1/$file | awk '$1 ~ /^.*w.*/ { print $NF; }')
|
||||||
|
|
||||||
|
if [ ! -z $wfile ]; then
|
||||||
|
# scaling_setspeed is a special file and we
|
||||||
|
# should skip updating it
|
||||||
|
if [ $file != "scaling_setspeed" ]; then
|
||||||
|
local val=$(cat $1/$file)
|
||||||
|
printf "Writing $val to: $file\n"
|
||||||
|
echo $val > $1/$file
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf "\n"
|
||||||
|
update_cpufreq_files_in_dir "$1/$file"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
printf "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update all writable files with their existing values
|
||||||
|
update_all_cpufreq_files()
|
||||||
|
{
|
||||||
|
printf "** Test: Running ${FUNCNAME[0]} **\n\n"
|
||||||
|
|
||||||
|
update_cpufreq_files_in_dir $CPUFREQROOT
|
||||||
|
|
||||||
|
printf "%s\n\n" "------------------------------------------------"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# CHANGE CPU FREQUENCIES
|
||||||
|
|
||||||
|
# $1: policy
|
||||||
|
find_current_freq()
|
||||||
|
{
|
||||||
|
cat $CPUFREQROOT/$1/scaling_cur_freq
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1: policy
|
||||||
|
# $2: frequency
|
||||||
|
set_cpu_frequency()
|
||||||
|
{
|
||||||
|
printf "Change frequency for $1 to $2\n"
|
||||||
|
echo $2 > $CPUFREQROOT/$1/scaling_setspeed
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1: policy
|
||||||
|
test_all_frequencies()
|
||||||
|
{
|
||||||
|
local filepath="$CPUFREQROOT/$1"
|
||||||
|
|
||||||
|
backup_governor $1
|
||||||
|
|
||||||
|
local found=$(switch_governor $1 "userspace")
|
||||||
|
if [ $found = 1 ]; then
|
||||||
|
printf "${FUNCNAME[0]}: userspace governor not available for: $1\n"
|
||||||
|
return;
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "Switched governor for $1 to userspace\n\n"
|
||||||
|
|
||||||
|
local freqs=$(cat $filepath/scaling_available_frequencies)
|
||||||
|
printf "Available frequencies for $1: $freqs\n\n"
|
||||||
|
|
||||||
|
# Set all frequencies one-by-one
|
||||||
|
for freq in $freqs; do
|
||||||
|
set_cpu_frequency $1 $freq
|
||||||
|
done
|
||||||
|
|
||||||
|
printf "\n"
|
||||||
|
|
||||||
|
restore_governor $1
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1: loop count
|
||||||
|
shuffle_frequency_for_all_cpus()
|
||||||
|
{
|
||||||
|
printf "** Test: Running ${FUNCNAME[0]} for $1 loops **\n\n"
|
||||||
|
|
||||||
|
for i in `seq 1 $1`; do
|
||||||
|
for_each_policy test_all_frequencies
|
||||||
|
done
|
||||||
|
printf "\n%s\n\n" "------------------------------------------------"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Basic cpufreq tests
|
||||||
|
cpufreq_basic_tests()
|
||||||
|
{
|
||||||
|
printf "*** RUNNING CPUFREQ SANITY TESTS ***\n"
|
||||||
|
printf "====================================\n\n"
|
||||||
|
|
||||||
|
count=$(count_cpufreq_managed_cpus)
|
||||||
|
if [ $count = 0 ]; then
|
||||||
|
printf "No cpu is managed by cpufreq core, exiting\n"
|
||||||
|
exit;
|
||||||
|
else
|
||||||
|
printf "CPUFreq manages: $count CPUs\n\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect & print which CPUs are not managed by cpufreq
|
||||||
|
print_unmanaged_cpus
|
||||||
|
|
||||||
|
# read/update all cpufreq files
|
||||||
|
read_all_cpufreq_files
|
||||||
|
update_all_cpufreq_files
|
||||||
|
|
||||||
|
# hotplug cpus
|
||||||
|
reboot_cpus 5
|
||||||
|
|
||||||
|
# Test all frequencies
|
||||||
|
shuffle_frequency_for_all_cpus 2
|
||||||
|
|
||||||
|
# Test all governors
|
||||||
|
shuffle_governors_for_all_cpus 1
|
||||||
|
}
|
146
tools/testing/selftests/cpufreq/governor.sh
Executable file
146
tools/testing/selftests/cpufreq/governor.sh
Executable file
@ -0,0 +1,146 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Test governors
|
||||||
|
|
||||||
|
# protect against multiple inclusion
|
||||||
|
if [ $FILE_GOVERNOR ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
FILE_GOVERNOR=DONE
|
||||||
|
fi
|
||||||
|
|
||||||
|
source cpu.sh
|
||||||
|
source cpufreq.sh
|
||||||
|
|
||||||
|
CUR_GOV=
|
||||||
|
CUR_FREQ=
|
||||||
|
|
||||||
|
# Find governor's directory path
|
||||||
|
# $1: policy, $2: governor
|
||||||
|
find_gov_directory()
|
||||||
|
{
|
||||||
|
if [ -d $CPUFREQROOT/$2 ]; then
|
||||||
|
printf "$CPUFREQROOT/$2\n"
|
||||||
|
elif [ -d $CPUFREQROOT/$1/$2 ]; then
|
||||||
|
printf "$CPUFREQROOT/$1/$2\n"
|
||||||
|
else
|
||||||
|
printf "INVALID\n"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1: policy
|
||||||
|
find_current_governor()
|
||||||
|
{
|
||||||
|
cat $CPUFREQROOT/$1/scaling_governor
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1: policy
|
||||||
|
backup_governor()
|
||||||
|
{
|
||||||
|
CUR_GOV=$(find_current_governor $1)
|
||||||
|
|
||||||
|
printf "Governor backup done for $1: $CUR_GOV\n"
|
||||||
|
|
||||||
|
if [ $CUR_GOV == "userspace" ]; then
|
||||||
|
CUR_FREQ=$(find_current_freq $1)
|
||||||
|
printf "Governor frequency backup done for $1: $CUR_FREQ\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1: policy
|
||||||
|
restore_governor()
|
||||||
|
{
|
||||||
|
__switch_governor $1 $CUR_GOV
|
||||||
|
|
||||||
|
printf "Governor restored for $1 to $CUR_GOV\n"
|
||||||
|
|
||||||
|
if [ $CUR_GOV == "userspace" ]; then
|
||||||
|
set_cpu_frequency $1 $CUR_FREQ
|
||||||
|
printf "Governor frequency restored for $1: $CUR_FREQ\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
# param:
|
||||||
|
# $1: policy, $2: governor
|
||||||
|
__switch_governor()
|
||||||
|
{
|
||||||
|
echo $2 > $CPUFREQROOT/$1/scaling_governor
|
||||||
|
}
|
||||||
|
|
||||||
|
# SWITCH GOVERNORS
|
||||||
|
|
||||||
|
# $1: cpu, $2: governor
|
||||||
|
switch_governor()
|
||||||
|
{
|
||||||
|
local filepath=$CPUFREQROOT/$1/scaling_available_governors
|
||||||
|
|
||||||
|
# check if governor is available
|
||||||
|
local found=$(cat $filepath | grep $2 | wc -l)
|
||||||
|
if [ $found = 0 ]; then
|
||||||
|
echo 1;
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
__switch_governor $1 $2
|
||||||
|
echo 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1: policy, $2: governor
|
||||||
|
switch_show_governor()
|
||||||
|
{
|
||||||
|
cur_gov=find_current_governor
|
||||||
|
if [ $cur_gov == "userspace" ]; then
|
||||||
|
cur_freq=find_current_freq
|
||||||
|
fi
|
||||||
|
|
||||||
|
# switch governor
|
||||||
|
__switch_governor $1 $2
|
||||||
|
|
||||||
|
printf "\nSwitched governor for $1 to $2\n\n"
|
||||||
|
|
||||||
|
if [ $2 == "userspace" -o $2 == "powersave" -o $2 == "performance" ]; then
|
||||||
|
printf "No files to read for $2 governor\n\n"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# show governor files
|
||||||
|
local govpath=$(find_gov_directory $1 $2)
|
||||||
|
read_cpufreq_files_in_dir $govpath
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1: function to be called, $2: policy
|
||||||
|
call_for_each_governor()
|
||||||
|
{
|
||||||
|
local filepath=$CPUFREQROOT/$2/scaling_available_governors
|
||||||
|
|
||||||
|
# Exit if cpu isn't managed by cpufreq core
|
||||||
|
if [ ! -f $filepath ]; then
|
||||||
|
return;
|
||||||
|
fi
|
||||||
|
|
||||||
|
backup_governor $2
|
||||||
|
|
||||||
|
local governors=$(cat $filepath)
|
||||||
|
printf "Available governors for $2: $governors\n"
|
||||||
|
|
||||||
|
for governor in $governors; do
|
||||||
|
$1 $2 $governor
|
||||||
|
done
|
||||||
|
|
||||||
|
restore_governor $2
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1: loop count
|
||||||
|
shuffle_governors_for_all_cpus()
|
||||||
|
{
|
||||||
|
printf "** Test: Running ${FUNCNAME[0]} for $1 loops **\n\n"
|
||||||
|
|
||||||
|
for i in `seq 1 $1`; do
|
||||||
|
for_each_policy call_for_each_governor switch_show_governor
|
||||||
|
done
|
||||||
|
printf "%s\n\n" "------------------------------------------------"
|
||||||
|
}
|
128
tools/testing/selftests/cpufreq/main.sh
Executable file
128
tools/testing/selftests/cpufreq/main.sh
Executable file
@ -0,0 +1,128 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
source cpu.sh
|
||||||
|
source cpufreq.sh
|
||||||
|
source governor.sh
|
||||||
|
|
||||||
|
FUNC=basic # do basic tests by default
|
||||||
|
OUTFILE=cpufreq_selftest
|
||||||
|
SYSFS=
|
||||||
|
CPUROOT=
|
||||||
|
CPUFREQROOT=
|
||||||
|
|
||||||
|
helpme()
|
||||||
|
{
|
||||||
|
printf "Usage: $0 [-h] [-to args]
|
||||||
|
[-h <help>]
|
||||||
|
[-o <output-file-for-dump>]
|
||||||
|
[-t <basic: Basic cpufreq testing>]
|
||||||
|
\n"
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
prerequisite()
|
||||||
|
{
|
||||||
|
msg="skip all tests:"
|
||||||
|
|
||||||
|
if [ $UID != 0 ]; then
|
||||||
|
echo $msg must be run as root >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
taskset -p 01 $$
|
||||||
|
|
||||||
|
SYSFS=`mount -t sysfs | head -1 | awk '{ print $3 }'`
|
||||||
|
|
||||||
|
if [ ! -d "$SYSFS" ]; then
|
||||||
|
echo $msg sysfs is not mounted >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
CPUROOT=$SYSFS/devices/system/cpu
|
||||||
|
CPUFREQROOT="$CPUROOT/cpufreq"
|
||||||
|
|
||||||
|
if ! ls $CPUROOT/cpu* > /dev/null 2>&1; then
|
||||||
|
echo $msg cpus not available in sysfs >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! ls $CPUROOT/cpufreq > /dev/null 2>&1; then
|
||||||
|
echo $msg cpufreq directory not available in sysfs >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_arguments()
|
||||||
|
{
|
||||||
|
while getopts ht:o: arg
|
||||||
|
do
|
||||||
|
case $arg in
|
||||||
|
h) # --help
|
||||||
|
helpme
|
||||||
|
;;
|
||||||
|
|
||||||
|
t) # --func_type (Function to perform: basic (default: basic))
|
||||||
|
FUNC=$OPTARG
|
||||||
|
;;
|
||||||
|
|
||||||
|
o) # --output-file (Output file to store dumps)
|
||||||
|
OUTFILE=$OPTARG
|
||||||
|
;;
|
||||||
|
|
||||||
|
\?)
|
||||||
|
helpme
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
do_test()
|
||||||
|
{
|
||||||
|
# Check if CPUs are managed by cpufreq or not
|
||||||
|
count=$(count_cpufreq_managed_cpus)
|
||||||
|
|
||||||
|
if [ $count = 0 ]; then
|
||||||
|
echo "No cpu is managed by cpufreq core, exiting"
|
||||||
|
exit 2;
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$FUNC" in
|
||||||
|
"basic")
|
||||||
|
cpufreq_basic_tests
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "Invalid [-f] function type"
|
||||||
|
helpme
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# clear dumps
|
||||||
|
# $1: file name
|
||||||
|
clear_dumps()
|
||||||
|
{
|
||||||
|
echo "" > $1.txt
|
||||||
|
echo "" > $1.dmesg_cpufreq.txt
|
||||||
|
echo "" > $1.dmesg_full.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1: output file name
|
||||||
|
dmesg_dumps()
|
||||||
|
{
|
||||||
|
dmesg | grep cpufreq >> $1.dmesg_cpufreq.txt
|
||||||
|
|
||||||
|
# We may need the full logs as well
|
||||||
|
dmesg >> $1.dmesg_full.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse arguments
|
||||||
|
parse_arguments $@
|
||||||
|
|
||||||
|
# Make sure all requirements are met
|
||||||
|
prerequisite
|
||||||
|
|
||||||
|
# Run requested functions
|
||||||
|
clear_dumps $OUTFILE
|
||||||
|
do_test >> $OUTFILE.txt
|
||||||
|
dmesg_dumps $OUTFILE
|
Loading…
Reference in New Issue
Block a user