Skip to content

cpu/stressng_cpu: Add runtime scheduling-policy change test - #3238

Open
SamirMulani wants to merge 1 commit into
avocado-framework-tests:masterfrom
SamirMulani:stress_ng_resetting_sched_policy
Open

cpu/stressng_cpu: Add runtime scheduling-policy change test#3238
SamirMulani wants to merge 1 commit into
avocado-framework-tests:masterfrom
SamirMulani:stress_ng_resetting_sched_policy

Conversation

@SamirMulani

Copy link
Copy Markdown
Contributor

Add test_runtime_policy_change() to validate that the Linux kernel correctly applies sched_setscheduler(2) on live stress-ng worker threads at run-time.

The test spawns stress-ng CPU worker threads in the background and walks the following policy transition chain on all worker PIDs:

SCHED_OTHER -> SCHED_IDLE -> SCHED_BATCH -> SCHED_FIFO -> SCHED_RR -> SCHED_OTHER

SCHED_DEADLINE is excluded as it requires sched_setattr(2) with runtime/deadline/period attributes which are not exposed via the Python os.sched_setscheduler() interface.

Each transition is verified using two sources:

  • os.sched_getscheduler(pid) : direct kernel syscall (primary)
  • chrt -p : userspace scheduling tool (secondary)

All transition results are collected in a list and reported at the end so a single run shows the complete picture of pass/fail/skip rather than stopping at the first failure.

Also introduce POLICY_META as a single source of truth for policy attributes (name, label, rt_prio) and derive CHRT_POLICY_FLAG from it via a dict comprehension to avoid duplicate policy definitions.

New YAML parameters:

  • policy_change_runtime: seconds stress-ng workers stay alive (default 120)
  • policy_change_workers: number of --cpu worker threads to spawn (default 4)

@PraveenPenguin
PraveenPenguin requested a review from sacsant July 29, 2026 05:36
Comment thread cpu/stressng_cpu.py

Background
----------
By default, processes run under CFS (SCHED_OTHER, policy=0).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will suggest to only retain this content under background. Remaining content is not required and is too verbose

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trimmed the Background section to a single essential paragraph. Removed the transition chain diagram, SCHED_DEADLINE explanation, Verification Method, and Test Flow sections.

Comment thread cpu/stressng_cpu.py Outdated
self.log.info("Test: Runtime Scheduling-Policy Change")
self.log.info("=" * 60)

# Aggregate results across all transitions and all PIDs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment not required.

Comment thread cpu/stressng_cpu.py Outdated

# ----------------------------------------------------------------
# Step 1: Spawn stress-ng in the background
# ----------------------------------------------------------------

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment is unnecessary

Comment thread cpu/stressng_cpu.py Outdated

# ----------------------------------------------------------------
# Step 2: Discover worker PIDs
# ----------------------------------------------------------------

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment is unnecessary

Comment thread cpu/stressng_cpu.py Outdated

# ----------------------------------------------------------------
# Step 3: Walk through the policy transition chain
# ----------------------------------------------------------------

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment is unnecessary

Comment thread cpu/stressng_cpu.py Outdated
"pgrep found no children of PID %d", parent_pid)

# Always include the parent itself so we have at least one target
pids.add(parent_pid)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_get_stressng_worker_pids unconditionally adds parent_pid to pids with pids.add(parent_pid) at the end. This means worker_pids can never be empty. The guard:

if not worker_pids:
self._terminate_stressng()
self.fail("No stress-ng worker PIDs found — cannot proceed.")

…is dead code and will never trigger. The real case of interest — no children (workers) found — is silently swallowed, and the test proceeds exercising only the parent PID (the stress-ng manager process, not a CPU stressor).

Suggested to check for children separately from the parent.

Comment thread cpu/stressng_cpu.py Outdated

# Derived from POLICY_META so label strings are never duplicated.
# chrt -p <pid> uses "--<label>" as the policy flag.
CHRT_POLICY_FLAG = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is never used anywhere in the code. Is this required?

Comment thread cpu/stressng_cpu.py Outdated

def _read_proc_sched_policy(self, pid):
"""
Read the current scheduling policy integer for *pid* from

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring opens with "Read the current scheduling policy integer for pid from /proc//status" and then contradicts itself in the same sentence, describing what actually happens (using os.sched_getscheduler()). The /proc//status reference is wrong — that file does not contain the scheduling policy integer — and will confuse anyone reading this code.

Please change the docstring accordingly

Comment thread cpu/stressng_cpu.py
Parameters (from YAML)
----------------------
- ``policy_change_runtime`` : seconds stress-ng runs (default 120)
- ``policy_change_workers`` : number of CPU stressor threads

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also this contradicts with default value mentioned in YAML

Comment thread cpu/stressng_cpu.py Outdated
except (subprocess.CalledProcessError, FileNotFoundError) as chrt_err:
self.log.warning(
"chrt verification skipped for PID %d: %s", pid, chrt_err)
return True, "chrt-unavailable"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When chrt is not installed (FileNotFoundError) or fails (CalledProcessError), the method returns (True, "chrt-unavailable"). This means the secondary verification silently succeeds — the test may pass while only the primary os.sched_getscheduler() check actually ran. This is particularly concerning because the PR's stated purpose is dual verification. There is no warning to the user that the secondary verification was skipped for the whole test run (not just one PID).

@SamirMulani SamirMulani Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Split the combined except (subprocess.CalledProcessError, FileNotFoundError) into separate handlers. FileNotFoundError now returns (False, "chrt-not-installed") and emits a warning indicating that secondary verification is unavailable for the entire test run. CalledProcessError returns (False, "chrt-error") with a per-PID warning. Both cases are now reported as actual failures in all_results instead of being treated as successful secondary verification.

@SamirMulani
SamirMulani force-pushed the stress_ng_resetting_sched_policy branch from 6cc872e to c5859f8 Compare July 30, 2026 11:10
@SamirMulani

Copy link
Copy Markdown
Contributor Author
avocado run stressng_cpu.py -m stressng_cpu.py.data/stressng_cpu.yaml --max-parallel-tasks=1
Fetching asset from stressng_cpu.py:Stressngcpu.test_cpu
Fetching asset from stressng_cpu.py:Stressngcpu.test_runtime_policy_change
JOB ID     : 72f05568be7101d2c3012eb1f2c9daa33e4f4b43
JOB LOG    : /root/avocado-fvt-wrapper/results/job-2026-07-30T12.25-72f0556/job.log
 (1/2) stressng_cpu.py:Stressngcpu.test_cpu;run-test_mode-428f: STARTED

 (1/2) stressng_cpu.py:Stressngcpu.test_cpu;run-test_mode-428f: PASS (672.98 s)
 (2/2) stressng_cpu.py:Stressngcpu.test_runtime_policy_change;run-test_mode-428f: STARTED
 (2/2) stressng_cpu.py:Stressngcpu.test_runtime_policy_change;run-test_mode-428f: PASS (76.54 s)
RESULTS    : PASS 2 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 | CANCEL 0
JOB HTML   : /root/avocado-fvt-wrapper/results/job-2026-07-30T12.25-72f0556/results.html
JOB TIME   : 765.99 s

@PraveenPenguin
PraveenPenguin requested a review from sacsant July 31, 2026 06:08
@Naresh-ibm Naresh-ibm self-assigned this Aug 3, 2026
Comment thread cpu/stressng_cpu.py
child_pids = [p for p in worker_pids
if p != self._stressng_proc.pid]
if not child_pids:
self._terminate_stressng()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_terminate_stressng() sets self._stressng_proc = None as its first action (line 682) before doing anything else. After calling _terminate_stressng() on line 544, the very next line (548) dereferences self._stressng_proc.pid, which will raise AttributeError: 'NoneType' object has no attribute 'pid' and mask the real failure with an uncaught exception instead of the intended self.fail() message.

Current Code:

line 543-548
if not child_pids:
self._terminate_stressng() # sets self._stressng_proc = None !
self.fail(
"No stress-ng worker child PIDs found under PID %d — "
"cannot exercise CPU stressor scheduling policies."
% self._stressng_proc.pid) # <-- AttributeError: None.pid

Suggested Fix:

Capture the PID before terminating:

if not child_pids:
mgr_pid = self._stressng_proc.pid
self._terminate_stressng()
self.fail(
"No stress-ng worker child PIDs found under PID %d — "
"cannot exercise CPU stressor scheduling policies." % mgr_pid)

Comment thread cpu/stressng_cpu.py

Uses two independent sources:
1. ``os.sched_getscheduler(pid)`` — kernel syscall (primary)
2. ``chrt -p <pid>`` — userspace tool (secondary)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

verify_policy_via_chrt() returns (False, "chrt-not-installed") or (False, "chrt-error") when chrt is absent or fails. In _verify_policy_for_pids(), any False from chrt_ok is unconditionally appended to failures[], which then causes self.fail(). This means the entire test will be marked FAILED on any system where chrt is not installed or where it temporarily fails (e.g. PID disappeared between set and verify). The PR description calls chrt a secondary check, but the code treats it as primary and mandatory. The FileNotFoundError warning at line 385 claims "secondary policy verification is unavailable for the entire test run" — but the loop never exits early after that; it will emit this warning for every PID of every transition, flooding the log.

Current Code:
_verify_policy_for_pids, lines 487-495

chrt_ok, chrt_actual = self._verify_policy_via_chrt(pid, expected_policy)
if not chrt_ok:
    failures.append(
        "PID %d: chrt mismatch — expected %s got %s"
        % (pid, policy_name, chrt_actual))

Suggested Fix:

Distinguish between chrt tool errors (warn only) and actual policy mismatches:

chrt_ok, chrt_actual = self._verify_policy_via_chrt(pid, expected_policy)
if not chrt_ok:
    if chrt_actual in ("chrt-not-installed", "chrt-error", "parse-error"):
        # Secondary tool unavailable; primary syscall result is authoritative
        self.log.warning(
            "PID %d: chrt secondary check skipped (%s)",
            pid, chrt_actual)
    else:
        failures.append(
            "PID %d: chrt mismatch — expected %s got %s"
            % (pid, policy_name, chrt_actual))

Also, track whether chrt is available once (e.g. a self._chrt_available flag set in setUp) and skip all secondary checks early rather than spawning a new chrt subprocess for every PID of every transition.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree

Comment thread cpu/stressng_cpu.py
proc = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you check on this?

Add test_runtime_policy_change() to validate that the Linux kernel
correctly applies sched_setscheduler(2) on live stress-ng worker
threads at run-time.

The test spawns stress-ng CPU worker threads in the background and
walks the following policy transition chain on all worker PIDs:

  SCHED_OTHER -> SCHED_IDLE -> SCHED_BATCH -> SCHED_FIFO -> SCHED_RR -> SCHED_OTHER

SCHED_DEADLINE is excluded as it requires sched_setattr(2) with
runtime/deadline/period attributes which are not exposed via the
Python os.sched_setscheduler() interface.

Each transition is verified using two sources:
  - os.sched_getscheduler(pid) : direct kernel syscall (primary)
  - chrt -p <pid>              : userspace scheduling tool (secondary)

All transition results are collected in a list and reported at the end
so a single run shows the complete picture of pass/fail/skip rather
than stopping at the first failure.

Also introduce POLICY_META as a single source of truth for policy
attributes (name, label, rt_prio) and derive CHRT_POLICY_FLAG from it
via a dict comprehension to avoid duplicate policy definitions.

New YAML parameters:
  - policy_change_runtime: seconds stress-ng workers stay alive (default 120)
  - policy_change_workers: number of --cpu worker threads to spawn (default 4)

Signed-off-by: Samir Mulani <samir@linux.ibm.com>
@SamirMulani
SamirMulani force-pushed the stress_ng_resetting_sched_policy branch from 0a61576 to 423125d Compare August 12, 2026 12:26
@SamirMulani

Copy link
Copy Markdown
Contributor Author
JOB ID     : 461313e126c439cffdc91714ea9aa00ae74504bd
JOB LOG    : /root/avocado-fvt-wrapper/results/job-2026-08-12T16.08-461313e/job.log
 (1/2) stressng_cpu.py:Stressngcpu.test_cpu;run-test_mode-08be: STARTED
 (1/2) stressng_cpu.py:Stressngcpu.test_cpu;run-test_mode-08be:  PASS (1194.31 s)
 (2/2) stressng_cpu.py:Stressngcpu.test_runtime_policy_change;run-test_mode-08be: STARTED
 (2/2) stressng_cpu.py:Stressngcpu.test_runtime_policy_change;run-test_mode-08be:  PASS (134.86 s)
RESULTS    : PASS 2 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 | CANCEL 0
JOB HTML   : /root/avocado-fvt-wrapper/results/job-2026-08-12T16.08-461313e/results.html
JOB TIME   : 1352.00 s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants