Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# CPUFreq_Validation

## Overview

The `CPUFreq_Validation` test validates the CPU frequency scaling capabilities of a system using the Linux `cpufreq` subsystem. It verifies the ability to set and reflect CPU frequencies across shared policy domains (e.g., clusters of CPUs sharing frequency control).

This test is designed to be **SoC-agnostic**, supporting platforms with per-policy frequency management (e.g., Qualcomm SoCs with `policy0`, `policy4`, etc.).

## Test Goals

- Ensure all cpufreq policies are present and functional
- Iterate through all available frequencies and validate correct scaling
- Ensure that CPU governors can be set to `userspace`
- Provide robust reporting per policy (e.g., `CPU0-3 [via policy0] = PASS`)
- Avoid flaky failures in CI by using retries and proper checks

## Prerequisites

- Kernel must be built with `CONFIG_CPU_FREQ` and `CONFIG_CPU_FREQ_GOV_USERSPACE`
- `sysfs` access to `/sys/devices/system/cpu/cpufreq/*`
- Root privileges (to write to cpufreq entries)

## Script Location

```
Runner/suites/Kernel/FunctionalArea/baseport/CPUFreq_Validation/run.sh
```

## Files

- `run.sh` - Main test script
- `CPUFreq_Validation.res` - Summary result file with PASS/FAIL
- `CPUFreq_Validation.log` - Full execution log (generated if logging is enabled)

## How It Works

1. The script detects all cpufreq policies under `/sys/devices/system/cpu/cpufreq/`
2. For each policy:
- Reads the list of related CPUs
- Attempts to set each available frequency using the `userspace` governor
- Verifies that the frequency was correctly applied
3. The result is logged per policy
4. The overall test passes only if all policies succeed

## Example Output

```
[INFO] CPU0-3 [via policy0] = PASS
[FAIL] CPU4-6 [via policy4] = FAIL
[INFO] CPU7 [via policy7] = PASS
```

## Return Code

- `0` — All policies passed
- `1` — One or more policies failed

## Integration in CI

- Can be run standalone or via LAVA
- Result file `CPUFreq_Validation.res` will be parsed by `result_parse.sh`

## Notes

- Some CPUs may share frequency domains, so per-core testing is not reliable
- The test includes retries to reduce false failures due to transient conditions

## License

SPDX-License-Identifier: BSD-3-Clause-Clear
(c) Qualcomm Technologies, Inc. and/or its subsidiaries.

169 changes: 53 additions & 116 deletions Runner/suites/Kernel/FunctionalArea/baseport/CPUFreq_Validation/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,162 +20,99 @@ if [ -z "$INIT_ENV" ]; then
exit 1
fi

# Only source if not already loaded (idempotent)
if [ -z "$__INIT_ENV_LOADED" ]; then
# shellcheck disable=SC1090
. "$INIT_ENV"
fi
# Always source functestlib.sh, using $TOOLS exported by init_env
# shellcheck disable=SC1090,SC1091
. "$TOOLS/functestlib.sh"

TESTNAME="CPUFreq_Validation"
test_path=$(find_test_case_by_name "$TESTNAME")
cd "$test_path" || exit 1
# shellcheck disable=SC2034
res_file="./$TESTNAME.res"

log_info "-----------------------------------------------------------------------------------------"
log_info "-------------------Starting $TESTNAME Testcase----------------------------"
log_info "=== CPUFreq Frequency Walker with Retry and Cleanup ==="

NUM_CPUS=$(nproc)
log_info "Detected $NUM_CPUS CPU cores."
log_info "------------------------------------------------------------"
log_info "Starting $TESTNAME Testcase"

overall_pass=0
status_dir="/tmp/cpufreq_status.$$"
mkdir -p "$status_dir"

validate_cpu_core() {
local cpu="$1"
local core_id="$2"
local status_file="$status_dir/core_$core_id"
echo "unknown" > "$status_file"

log_info "Processing $cpu..."

local cpu_num
cpu_num=$(basename "$cpu" | tr -dc '0-9')
if [ -f "/sys/devices/system/cpu/cpu$cpu_num/online" ]; then
echo 1 > "/sys/devices/system/cpu/cpu$cpu_num/online"
fi

if [ ! -d "$cpu/cpufreq" ]; then
log_info "[SKIP] $cpu does not support cpufreq."
echo "skip" > "$status_file"
return
fi
for policy_dir in /sys/devices/system/cpu/cpufreq/policy*; do
policy=$(basename "$policy_dir")

local freqs_file="$cpu/cpufreq/scaling_available_frequencies"
read -r available_freqs < "$freqs_file" 2>/dev/null
if [ -z "$available_freqs" ]; then
log_info "[SKIP] No available frequencies for $cpu"
echo "skip" > "$status_file"
return
if [ ! -d "$policy_dir" ]; then
log_warn "Skipping $policy_dir, not a directory"
continue
fi

local original_governor
original_governor=$(cat "$cpu/cpufreq/scaling_governor" 2>/dev/null)

if echo "userspace" > "$cpu/cpufreq/scaling_governor"; then
log_info "[INFO] Set governor to userspace."
sync
sleep 0.5
else
log_error "Cannot set userspace governor for $cpu."
echo "fail" > "$status_file"
return
cpus=$(cat "$policy_dir/related_cpus" 2>/dev/null)
[ -z "$cpus" ] && {
log_warn "No related CPUs found for $policy_dir"
continue
}

available_freqs=$(cat "$policy_dir/scaling_available_frequencies" 2>/dev/null)
[ -z "$available_freqs" ] && {
log_warn "No available frequencies for $policy_dir"
continue
}

original_governor=$(cat "$policy_dir/scaling_governor" 2>/dev/null)
if ! echo "userspace" > "$policy_dir/scaling_governor"; then
log_fail "$policy_dir: Unable to set userspace governor"
echo "fail" > "$status_dir/$policy"
overall_pass=1
continue
fi

echo "pass" > "$status_file"
echo "pass" > "$status_dir/$policy"

for freq in $available_freqs; do
log_info "Setting $cpu to frequency $freq kHz..."

echo "$freq" > "$cpu/cpufreq/scaling_min_freq" 2>/dev/null
echo "$freq" > "$cpu/cpufreq/scaling_max_freq" 2>/dev/null

if ! echo "$freq" > "$cpu/cpufreq/scaling_setspeed" 2>/dev/null; then
log_error "[SKIP] Kernel rejected freq $freq for $cpu"
log_info "$policy: Trying frequency $freq"
echo "$freq" > "$policy_dir/scaling_min_freq" 2>/dev/null
echo "$freq" > "$policy_dir/scaling_max_freq" 2>/dev/null
if ! echo "$freq" > "$policy_dir/scaling_setspeed" 2>/dev/null; then
log_warn "$policy: Kernel rejected frequency $freq"
continue
fi

retry=0
success=0
while [ "$retry" -lt 5 ]; do
cur=$(cat "$cpu/cpufreq/scaling_cur_freq")
if [ "$cur" = "$freq" ]; then
log_info "[PASS] $cpu set to $freq kHz."
success=1
break
fi
sleep 0.2
retry=$((retry + 1))
done

if [ "$success" -eq 0 ]; then
log_info "[RETRY] Re-attempting to set $cpu to $freq kHz..."
echo "$freq" > "$cpu/cpufreq/scaling_setspeed"
sleep 0.3
cur=$(cat "$cpu/cpufreq/scaling_cur_freq")
if [ "$cur" = "$freq" ]; then
log_info "[PASS-after-retry] $cpu set to $freq kHz."
else
log_error "[FAIL] $cpu failed to set $freq kHz twice. Current: $cur"
echo "fail" > "$status_file"
fi
sleep 0.3
cur_freq=$(cat "$policy_dir/scaling_cur_freq" 2>/dev/null)

if [ "$cur_freq" = "$freq" ]; then
log_info "[PASS] $policy reached $freq kHz"
else
log_warn "Mismatch freq: tried $freq, got $cur_freq on $policy"
echo "fail" > "$status_dir/$policy"
overall_pass=1
fi
done

log_info "Restoring $cpu governor to '$original_governor'..."
echo "$original_governor" > "$cpu/cpufreq/scaling_governor"
echo 0 > "$cpu/cpufreq/scaling_min_freq" 2>/dev/null
echo 0 > "$cpu/cpufreq/scaling_max_freq" 2>/dev/null
}

cpu_index=0
for cpu in /sys/devices/system/cpu/cpu[0-9]*; do
validate_cpu_core "$cpu" "$cpu_index" &
cpu_index=$((cpu_index + 1))
echo "$original_governor" > "$policy_dir/scaling_governor"
done

wait

log_info ""
log_info "=== Per-Core Test Summary ==="
for status_file in "$status_dir"/core_*; do
idx=$(basename "$status_file" | cut -d_ -f2)
status=$(cat "$status_file")
case "$status" in
pass)
log_info "CPU$idx: [PASS]"
;;
fail)
log_error "CPU$idx: [FAIL]"
overall_pass=1
;;
skip)
log_info "CPU$idx: [SKIPPED]"
;;
*)
log_error "CPU$idx: [UNKNOWN STATUS]"
overall_pass=1
;;
esac
log_info "=== Per-Policy CPU Group Summary ==="
for f in "$status_dir"/*; do
policy=$(basename "$f")
result=$(cat "$f")
cpus=$(tr '\n' ' ' < "/sys/devices/system/cpu/cpufreq/$policy/related_cpus")
cpulist=$(echo "$cpus" | sed 's/ /,/g')
status_str=$(echo "$result" | tr '[:lower:]' '[:upper:]')
echo "CPU$cpulist [via $policy] = $status_str"
done

log_info ""
log_info "=== Overall CPUFreq Validation Result ==="

log_info "=== Final Result ==="
if [ "$overall_pass" -eq 0 ]; then
log_pass "$TESTNAME : Test Passed"
log_pass "$TESTNAME: All policies passed"
echo "$TESTNAME PASS" > "$res_file"
else
log_fail "$TESTNAME : Test Failed"
log_fail "$TESTNAME: One or more policies failed"
echo "$TESTNAME FAIL" > "$res_file"
fi

rm -r "$status_dir"
sync
sleep 1
rm -rf "$status_dir"
exit "$overall_pass"
1 change: 1 addition & 0 deletions Runner/utils/functestlib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ log_pass() { log "PASS" "$@"; }
log_fail() { log "FAIL" "$@"; }
log_error() { log "ERROR" "$@"; }
log_skip() { log "SKIP" "$@"; }
log_warn() { log "WARN" "$@"; }

# --- Dependency check ---
check_dependencies() {
Expand Down
Loading