Added UFS functional tests#220
Conversation
c1f2b5b to
68e6860
Compare
|
Please add commit messages |
|
Ensure the script has execute permissions |
76dc353 to
ce6cfca
Compare
|
Squash to single commit |
| - $PWD/utils/send-to-lava.sh $PWD/suites/Kernel/Baseport/smmu/smmu.res || true | ||
| - $PWD/suites/Kernel/Baseport/Storage/UFS_Validation/run.sh || true | ||
| - $PWD/utils/send-to-lava.sh $PWD/suites/Kernel/Baseport/Storage/UFS_Validation/UFS_Validation.res || true | ||
| - $PWD/suites/Kernel/Baseport/Storage/ufs_gear_lane/run.sh || true |
There was a problem hiding this comment.
Are these tests validated on qcom-next?
smuppand
left a comment
There was a problem hiding this comment.
These comments apply to the other tests as well. Please also take care of the https://github.com/qualcomm-linux/qcom-linux-testkit/actions/runs/19762511972/job/56772745461.
| - $PWD/suites/Kernel/Baseport/Storage/ufs_runtime_suspend_resume/run.sh || true | ||
| - $PWD/utils/send-to-lava.sh $PWD/suites/Kernel/Baseport/Storage/ufs_runtime_suspend_resume/ufs_runtime_suspend_resume.res || true | ||
| - $PWD/suites/Kernel/Baseport/Storage/ufs_write_booster/run.sh || true | ||
| - $PWD/utils/send-to-lava.sh $PWD/suites/Kernel/Baseport/Storage/ufs_write_booster/ufs_write_booster.res || true |
There was a problem hiding this comment.
I suggest enabling these tests in qcom-next instead of meta-qcom.
There was a problem hiding this comment.
Enabled the tests on qcom-next by adding the .yaml files for each test case.
| # Only source if not already loaded | ||
| if [ -z "$__INIT_ENV_LOADED" ]; then | ||
| # shellcheck disable=SC1090 | ||
| . "$INIT_ENV" |
There was a problem hiding this comment.
you never set __INIT_ENV_LOADED, so this guard is effectively useless if this script is ever sourced again.
There was a problem hiding this comment.
exporting the __INIT_ENV_LOADED variable after setting it to 1 once the source is loaded.
| log_info "--------------------------------------------------" | ||
| log_info "------------- Starting $TESTNAME Test ------------" | ||
|
|
||
| check_dependencies dd grep cut head tail udevadm sleep |
There was a problem hiding this comment.
cut, head, tail, udevadm are not used anywhere in this script. You do use findmnt and awk, and rely on readlink (though that's almost always present)
There was a problem hiding this comment.
Removed unused dependencies and checking only the required dependencies.
| OPTIONAL_CONFIGS="CONFIG_SCSI_UFSHCD_PLATFORM CONFIG_SCSI_UFSHCD_PCI CONFIG_SCSI_UFS_CDNS_PLATFORM CONFIG_SCSI_UFS_HISI CONFIG_SCSI_UFS_EXYNOS CONFIG_SCSI_UFS_ROCKCHIP CONFIG_SCSI_UFS_BSG" | ||
|
|
||
| log_info "Checking mandatory kernel configs for UFS..." | ||
| if ! check_kernel_config "$MANDATORY_CONFIGS" 2>/dev/null; then |
There was a problem hiding this comment.
"$MANDATORY_CONFIGS" is one arugment with a space inside, not two config names. That'll fail.
| [ -n "$missing_optional" ] && log_info "Optional configs not present but continuing:$missing_optional" | ||
|
|
||
| check_dt_nodes "/sys/bus/platform/devices/*ufs*" || { | ||
| echo "$TESTNAME SKIP" > "$res_file" |
There was a problem hiding this comment.
Include log_skip so it is visible to the user.
There was a problem hiding this comment.
added log_skip when UFS dt_nodes are missing.
| log_pass "UFS Gear and Lane validation passed" | ||
| log_info "Gear: $CURRENT_GEAR (Expected: HS_GEAR$EXPECTED_GEAR)" | ||
| log_info "Lane: $CURRENT_LANE (Expected: 2)" | ||
| scan_dmesg_errors "ufs" "$test_path" |
There was a problem hiding this comment.
Called twice on the PASS path
There was a problem hiding this comment.
Fixed the PASS criteria and updating to .res file only once all the test steps are successful.
| exit 1 | ||
| fi | ||
|
|
||
| scan_dmesg_errors "ufs" "$test_path" |
| log_info "Gear: $CURRENT_GEAR (Expected: HS_GEAR$EXPECTED_GEAR)" | ||
| log_info "Lane: $CURRENT_LANE (Expected: 2)" | ||
| scan_dmesg_errors "ufs" "$test_path" | ||
| echo "$TESTNAME PASS" > "$res_file" |
There was a problem hiding this comment.
.res file is written PASS twice
There was a problem hiding this comment.
Fixed the PASS criteria and updating to .res file only once all the test steps are successful.
|
|
||
| scan_dmesg_errors "ufs" "$test_path" | ||
| log_pass "$TESTNAME completed successfully" | ||
| echo "$TESTNAME PASS" > "$res_file" |
| fi | ||
|
|
||
| # Function to get the first matching node path | ||
| get_dt_node_path() { |
There was a problem hiding this comment.
Please use already available functions from functestlib.sh
ce6cfca to
4650502
Compare
| if [ "$UFS_CLOCK_SCALED" -ne 1 ] && [ "$UFS_CLOCK_GATED" -ne 1 ]; then | ||
| log_fail "UFS clock scaling & gating test failed" | ||
| echo "$TESTNAME FAIL" > "$res_file" | ||
| exit 1 |
There was a problem hiding this comment.
In Lava: keep in mind that if one of them fails and exits with non-zero status, the whole job terminates. So update all scripts to exit 0 for PASS/ FAIL/ SKIP
There was a problem hiding this comment.
Changed all the exit codes to zero irrespective of test result.
a499f84 to
c0e80ff
Compare
smuppand
left a comment
There was a problem hiding this comment.
Centralize common UFS pre-checks
Every script repeats:
- mandatory/optional config checks
- DT node check
- detect_ufs_partition_block
This should be a reusable helper in functestlib.sh, e.g.:
ufs_common_precheck that returns SKIP reason and exports UFS_BLOCK_DEV / UFS_SYSFS_BASE.
Less duplication = fewer inconsistencies.
|
|
||
| attempt=1 | ||
|
|
||
| echo "Checking status of node '$node_name', expecting: '$expected_status'" |
There was a problem hiding this comment.
Use log_info/log_warn/log_fail from functestlib (or keep it silent and let caller log).
There was a problem hiding this comment.
Removed echo statements and used logger functions to log the messages
| attempt=1 | ||
|
|
||
| echo "Checking status of node '$node_name', expecting: '$expected_status'" | ||
| echo "Will retry up to $retries times with $delay second(s) delay between attempts" |
There was a problem hiding this comment.
It's the same here as well.
There was a problem hiding this comment.
Removed echo statements and used logger functions to log the messages
| echo "Will retry up to $retries times with $delay second(s) delay between attempts" | ||
|
|
||
| while [ "$attempt" -le "$retries" ]; do | ||
| echo "Attempt $attempt of $retries..." |
There was a problem hiding this comment.
It's the same here as well.
There was a problem hiding this comment.
Removed echo statements and used logger functions to log the messages
| status=$(cat "$node_name") | ||
|
|
||
| if [ "$status" = "$expected_status" ]; then | ||
| echo "Success! Node '$node_name' has status: '$status'" |
There was a problem hiding this comment.
It's the same here as well.
There was a problem hiding this comment.
Removed echo statements and used logger functions to log the messages
| echo "Success! Node '$node_name' has status: '$status'" | ||
| return 0 | ||
| else | ||
| echo "Current status: '$status', waiting for: '$expected_status'" |
There was a problem hiding this comment.
It's the same here as well.
There was a problem hiding this comment.
Removed echo statements and used logger functions to log the messages
| # wait for 60 seconds for UFS driver to go to idle state | ||
| log_info "Waiting for 60 seconds for UFS clock to gate" | ||
| sync | ||
| sleep 60 |
There was a problem hiding this comment.
Wait times: 60s/180s hardcoded, will be flaky across boards.
Provide env overrides:
LOAD_MB=${LOAD_MB:-128}
IDLE_WAIT_SEC=${IDLE_WAIT_SEC:-60}
There was a problem hiding this comment.
Instead of waiting indefinitely implemented timeout mechanism.
| UFS_MAX_FREQ=$(cat "$UFS_MAX_FREQ_NODE" 2>/dev/null) | ||
| if [ -z "$UFS_MAX_FREQ" ]; then | ||
| log_skip "Failed to read max frequency from $UFS_MAX_FREQ_NODE" | ||
| echo "$TESTNAME FAIL" > "$res_file" |
There was a problem hiding this comment.
Failing to read max_freq currently leads to FAIL (good), but elsewhere similar read failures are treated as SKIP. Rule of thumb:
“Feature/sysfs node not present” → SKIP
“Node present but unreadable / invalid value” → FAIL (because kernel/userspace is broken)
|
|
||
| # Check for UFS clock max freq node and assign to variable | ||
| log_info "Checking for UFS max clock freq node..." | ||
| if ! check_dt_nodes "/sys/devices/platform/soc@0/*ufs*/devfreq/*ufs*/max_freq"; then |
There was a problem hiding this comment.
is too broad. Might match unreleated nodes or multiple controllers. Detect actual UFS dir once and reuse.
e.g., locate .../ufshcd* / .../ufs base and store as UFS_SYSFS.
| fi | ||
|
|
||
| # Clean up temp file | ||
| rm -f "$tmpfile" |
There was a problem hiding this comment.
For every test using temp files:
tmpfile=$(mktemp "$test_path/${TESTNAME}.XXXXXX") (fallback if mktemp not available)
trap 'rm -f "$tmpfile"' EXIT INT TERM
This avoids stale files and collisions.
There was a problem hiding this comment.
The script starts both dd and a continuous sync loop, but there is no trap to clean them up if the script is interrupted. Background processes can be left running on interruption or unexpected exit.
In LAVA/CI, orphaned dd or sync loops can interfere with later tests.
Recommended fix:
cleanup() {
[ -n "${DD_PID:-}" ] && kill "$DD_PID" 2>/dev/null || true
[ -n "${sync_pid:-}" ] && kill "$sync_pid" 2>/dev/null || true
[ -n "${tmpfile:-}" ] && rm -f "$tmpfile"
}
trap cleanup EXIT INT TERM
| if command -v stat >/dev/null 2>&1; then | ||
| stat --format="[INFO] Size: %s bytes File: %n" "$tmpfile" | ||
| else | ||
| find "$tmpfile" -printf "[INFO] Size: %s bytes File: %p\n" |
There was a problem hiding this comment.
find -printf is not POSIX and absent in busybox builds sometimes.
If stat isn’t available, just ls -l "$tmpfile" or wc -c < "$tmpfile".
There was a problem hiding this comment.
added fallbacks with word count if stat is not available
c0e80ff to
a09359a
Compare
| } | ||
|
|
||
| # Find the matching dt node | ||
| get_dt_node_path() { |
There was a problem hiding this comment.
expand via set -- $pattern with unquoted expansion (it will expand globs), then iterate.
Or require callers to pass unquoted patterns (not acceptable; will break static analysis and is fragile).
There was a problem hiding this comment.
the ufs_get_dt_node_path function uses set -- $pattern and iterates over the glob expansion to check the match.
| version2="$2" | ||
|
|
||
| # Convert hex to decimal for comparison | ||
| ver1_dec=$((version1)) |
There was a problem hiding this comment.
The spec version node returns the value in the format specified by JEDEC (E.g. 0x0310)
version1 is like 0x0310. POSIX arithmetic expansion supports bases, but $((version1)) treats version1 as a variable name, not the contents. In many shells this becomes 0 (or error), so version compare breaks.
ver1_dec=$((16#$(printf '%s' "$version1" | sed 's/^0x//')))
ver2_dec=$((16#$(printf '%s' "$version2" | sed 's/^0x//')))
Or simplest: avoid arithmetic compare; compare strings by known set
|
|
||
| UFS_MIN_FREQ=$(cat "$UFS_MIN_FREQ_NODE" 2>/dev/null) | ||
| if [ -z "$UFS_MIN_FREQ" ]; then | ||
| log_skip "Failed to read min frequency from $UFS_MIN_FREQ" |
There was a problem hiding this comment.
this is not a SKIP condition, you mark FAIL, but log_skip. Use log_fail
Also you mean Failed to read min frequency from $UFS_MIN_FREQ_NODE ?
There was a problem hiding this comment.
Updated the log_skip statement to log_fail and UFS_MIN_FREQ to UFS_MIN_FREQ_NODE
| fi | ||
| } | ||
|
|
||
| # UFS precheck function - validates common UFS requirements |
There was a problem hiding this comment.
You added:
get_dt_node_path
check_node_status
check_node_status_with_timeout
ufs_precheck_common
The repo already has extensive helpers (and you’ve standardized function names), this risks:
name collisions (same function name defined elsewhere)
If they don’t, at least namespace them: ufs_get_dt_node_path, ufs_check_node_status, etc., to avoid generic collisions.
There was a problem hiding this comment.
Updated all the ufs specific functions with prefix ufs_*
| } | ||
|
|
||
| # UFS precheck function - validates common UFS requirements | ||
| # Usage: ufs_precheck_common "$TESTNAME" "$test_path" "$res_file" |
There was a problem hiding this comment.
But function takes no args and uses logging + exports UFS_BLOCK_DEV.
Fix the comment and clearly document outputs:
exports UFS_BLOCK_DEV
returns 0/1
requires check_kernel_config, check_dt_nodes, detect_ufs_partition_block
There was a problem hiding this comment.
Updated the function documentation based on the function implementation
| tmpfile="/ufs_clock_scaling.tmp" | ||
|
|
||
| # Start dd in background | ||
| dd if=/dev/zero of="$tmpfile" bs=2M count=1024 >/dev/null 2>&1 & |
There was a problem hiding this comment.
Still it is not addressed.
Default smaller (64–256MB) and allow override via env:
LOAD_MB=${LOAD_MB:-256}
Or use count=128 (256MB at 2M blocks) as default.
| fi | ||
|
|
||
| # Get UFS version and determine expected gear | ||
| # shellcheck disable=SC3034 |
There was a problem hiding this comment.
SC3034 is about local in dash/ash etc, not relevant here. If you meant another warning, clean it up.
There was a problem hiding this comment.
Removed the shellcheck disable SC3034.
| } | ||
|
|
||
| # Function to check node status with retries by reading from a file | ||
| check_node_status() { |
There was a problem hiding this comment.
check_node_status* should handle empty reads explicitly
If cat fails, status="", and you log:
“Current status: ''” repeatedly
Suggestion:
When empty, log a distinct message (“node unreadable”) and maybe break earlier with FAIL/SKIP depending on node existence.
There was a problem hiding this comment.
If the node status is empty the function terminates the loop and returns 1 by adding a log_fail statement.
a09359a to
1bcd802
Compare
| # Final validation | ||
| log_pass "UFS Write Booster validation passed" | ||
|
|
||
| scan_dmesg_errors "$test_path" "ufs" |
There was a problem hiding this comment.
order got changed from scan_dmesg_errors "ufs" "$test_path" to scan_dmesg_errors "$test_path" "ufs"
There was a problem hiding this comment.
The argument order is same as the function implementation in functestlib.sh so kept unchanged
| tmpfile="/ufs_clock_scaling.tmp" | ||
|
|
||
| # Start dd in background | ||
| dd if=/dev/zero of="$tmpfile" bs=2M count=1024 >/dev/null 2>&1 & |
There was a problem hiding this comment.
Still not addressed. Potentially destructive load generation (2GB dd tempfile everywhere, no free-space guard).
| - REPO_PATH=$PWD | ||
| - cd Runner/suites/Kernel/Baseport/Storage/ufs_read_writes | ||
| - ./run.sh || true | ||
| - $REPO_PATH/Runner/utils/send-to-lava.sh ufs_read_writes.res || true No newline at end of file |
| - REPO_PATH=$PWD | ||
| - cd Runner/suites/Kernel/Baseport/Storage/ufs_read_writes | ||
| - ./run.sh || true | ||
| - $REPO_PATH/Runner/utils/send-to-lava.sh ufs_read_writes.res || true No newline at end of file |
There was a problem hiding this comment.
added newline at the EOF for all yaml files
| log_info "Validating UFS clock scaling & clock gating" | ||
| # Check for UFS clock freq node and assign to variable | ||
| log_info "Checking for UFS clock freq node..." | ||
| if UFS_CLOCK_FREQ_NODE=$(ufs_get_dt_node_path "/sys/devices/platform/soc@0/*ufs*/devfreq/*ufs*/cur_freq"); then |
There was a problem hiding this comment.
should be more generic using ufs_get_dt_node_path with multiple fallback globs.
There was a problem hiding this comment.
Getting the primary ufs controller where the rootfs is mounted and using that to get the ufs clock freq nodes
1bcd802 to
5e7343c
Compare
smuppand
left a comment
There was a problem hiding this comment.
Now the repo license is changed to BSD-3-Clause. Fix it in all the files.
scan_dmesg_errors argument order got changed. scan_dmesg_errors "$test_path" "ufs"
|
@ualcomm/qualcomm-linux-testing.triage This pull request has been marked as stale due to 30 days of inactivity. |
5e7343c to
3ab539a
Compare
This PR adds new tests to validate storage features for UFS - UFS Read/Writes - Verifies the UFS read/writes in the user partition - UFS Runtime Suspend/Resume - Ensure proper link_state transitions during load and no-load - UFS Clock Scaling/Gating - Ensures UFS clock is scaled and gated based on load and requirement - UFS Hibern8 - Validate active/hibern8 entry exit on-demand - UFS Write Booster - Check whether UFS Write Booster is enabled on write - UFS Gear Validation - Check gear switching on load Impact: These tests improve the functional coverage for UFS Signed-off-by: Jeevanandan Sandan <sandanka@qti.qualcomm.com>
3ab539a to
a20b359
Compare
smuppand
left a comment
There was a problem hiding this comment.
Have proper commit message along with body about the changes.
| fi | ||
|
|
||
| # Only source if not already loaded | ||
| if [ -z "$__INIT_ENV_LOADED" ]; then |
There was a problem hiding this comment.
The guard is inverted. This sources init_env only when __INIT_ENV_LOADED is already set. On a normal first-time run, init_env will not be sourced, but the script still immediately sources "$TOOLS/functestlib.sh"
recommended fix, applies to other run.sh files as well
if [ -z "${__INIT_ENV_LOADED:-}" ]; then
# shellcheck disable=SC1090
. "$INIT_ENV"
fi
| log_info "--------------------------------------------------" | ||
| log_info "------------- Starting $TESTNAME Test ------------" | ||
|
|
||
| check_dependencies dd sleep |
There was a problem hiding this comment.
The script also uses cat, tr, kill, sync, and rm, but only checks dd sleep.
| tmpfile="$test_path/${TESTNAME}.tmp" | ||
|
|
||
| # checking if necessary space is available in rootfs | ||
| ensure_rootfs_min_size 3 |
There was a problem hiding this comment.
Return value is ignored. If rootfs is too small, the test still starts a 2 GiB write, which can fill the filesystem or create noisy CI failures.
Recommended fix:
if ! ensure_rootfs_min_size 3; then
log_skip "Insufficient rootfs free space for UFS clock scaling load"
echo "$TESTNAME SKIP" > "$res_file"
exit 0
fi
This same fix applies to ufs_gear_check, ufs_hibern8, and ufs_write_booster.
| tmpfile="/ufs_clock_scaling.tmp" | ||
|
|
||
| # Start dd in background | ||
| dd if=/dev/zero of="$tmpfile" bs=2M count=1024 >/dev/null 2>&1 & |
There was a problem hiding this comment.
This writes a 2 GiB tmpfile in the test directory. This is heavy for CI, can be slow on smaller devices, and can cause space pressure. The test only needs enough sustained write pressure to trigger devfreq it does not necessarily need a fixed 2 GiB write on every board.
Recommended fix: Parameterize the load size and default to a smaller value, for example:
UFS_LOAD_MB="${UFS_LOAD_MB:-512}"
count=$((UFS_LOAD_MB / 2))
dd if=/dev/zero of="$tmpfile" bs=2M count="$count" >/dev/null 2>&1 &
Set larger values from YAML/LAVA only when needed.
| fi | ||
|
|
||
| # Clean up temp file | ||
| rm -f "$tmpfile" |
There was a problem hiding this comment.
The script starts both dd and a continuous sync loop, but there is no trap to clean them up if the script is interrupted. Background processes can be left running on interruption or unexpected exit.
In LAVA/CI, orphaned dd or sync loops can interfere with later tests.
Recommended fix:
cleanup() {
[ -n "${DD_PID:-}" ] && kill "$DD_PID" 2>/dev/null || true
[ -n "${sync_pid:-}" ] && kill "$sync_pid" 2>/dev/null || true
[ -n "${tmpfile:-}" ] && rm -f "$tmpfile"
}
trap cleanup EXIT INT TERM
| fi | ||
|
|
||
| log_info "Running basic read test on $block_dev (non-rootfs)..." | ||
| if echo | dd of=/dev/null iflag=direct 2>/dev/null; then |
There was a problem hiding this comment.
This checks whether dd accepts iflag=direct, but it does not test whether direct I/O works with the actual UFS block device.
Recommended fix: log it as “dd supports iflag=direct” rather than assuming the device will. Current fallback is acceptable because the later real command gates PASS/FAIL.
| DD_WRITE="dd if=/dev/zero of=$tmpfile bs=1M count=64 conv=fsync" | ||
| else | ||
| log_warn "'conv=fsync' not supported by dd. Using basic write." | ||
| DD_WRITE="dd if=/dev/zero of=$tmpfile bs=1M count=64" |
There was a problem hiding this comment.
The tmpfile path is unquoted inside the constructed command string. Avoid command strings and call dd directly in branches, or keep the path safe with positional execution. Since paths here are controlled, this is non-blocking.
| OPTIONAL_CONFIGS="CONFIG_SCSI_UFSHCD_PLATFORM CONFIG_SCSI_UFSHCD_PCI CONFIG_SCSI_UFS_CDNS_PLATFORM CONFIG_SCSI_UFS_HISI CONFIG_SCSI_UFS_EXYNOS CONFIG_SCSI_UFS_ROCKCHIP CONFIG_SCSI_UFS_BSG" | ||
|
|
||
| log_info "Checking mandatory kernel configs for UFS..." | ||
| if ! check_kernel_config "$MANDATORY_CONFIGS" 2>/dev/null; then |
There was a problem hiding this comment.
Redirecting check_kernel_config stderr hides useful diagnostic output. Also, because check_kernel_config already logs config status, suppressing part of its output can reduce triage quality.
CI logs may not clearly show exactly which mandatory config failed.
Recommended fix:
if ! check_kernel_config "$MANDATORY_CONFIGS"; then
or explicitly log the missing config after checking each one
| } | ||
|
|
||
| # Function to check node status with retries by reading from a file | ||
| ufs_check_node_status() { |
There was a problem hiding this comment.
This helper starts a background continuous sync loop every time it checks a node. This can create unnecessary I/O and make helper behavior surprising.
Recommended fix: Split into two helpers:
ufs_check_node_status() should only poll.
Callers that need sync pressure should start/stop sync explicitly or pass an option.
| return 2 | ||
| fi | ||
|
|
||
| dev=$(mount | awk -v mp="$user_partition" ' |
There was a problem hiding this comment.
Parsing mount output is less robust than findmnt, and findmnt is already used in some scripts.
Recommended fix:
dev=$(findmnt -n -o SOURCE "$user_partition" 2>/dev/null)
with /proc/mounts fallback when findmnt is unavailable.
No description provided.