From 6301eff2d16571b847924e959a498596556d7eb3 Mon Sep 17 00:00:00 2001 From: Axymorrsen Date: Wed, 29 Jul 2026 16:06:57 +0800 Subject: [PATCH 1/6] sync: add scheduler pass-4 local conflict paths --- sync/resolutions/16.0.9.401/pass4-scheduler-ours.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 sync/resolutions/16.0.9.401/pass4-scheduler-ours.txt diff --git a/sync/resolutions/16.0.9.401/pass4-scheduler-ours.txt b/sync/resolutions/16.0.9.401/pass4-scheduler-ours.txt new file mode 100644 index 000000000000..142ec50635b5 --- /dev/null +++ b/sync/resolutions/16.0.9.401/pass4-scheduler-ours.txt @@ -0,0 +1,10 @@ +arch/arm64/configs/gki_defconfig +include/asm-generic/vmlinux.lds.h +include/linux/sched.h +include/linux/sched/task.h +include/uapi/linux/sched.h +kernel/Kconfig.preempt +kernel/sched/core.c +kernel/sched/debug.c +kernel/sched/idle.c +kernel/time/tick-sched.c From 2ceb8877e0598d030a7e0f76ac3759bc8172a71b Mon Sep 17 00:00:00 2001 From: Axymorrsen Date: Wed, 29 Jul 2026 16:07:06 +0800 Subject: [PATCH 2/6] sync: add scheduler pass-4 official hook paths --- sync/resolutions/16.0.9.401/pass4-scheduler-theirs.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 sync/resolutions/16.0.9.401/pass4-scheduler-theirs.txt diff --git a/sync/resolutions/16.0.9.401/pass4-scheduler-theirs.txt b/sync/resolutions/16.0.9.401/pass4-scheduler-theirs.txt new file mode 100644 index 000000000000..7d98b2af6f56 --- /dev/null +++ b/sync/resolutions/16.0.9.401/pass4-scheduler-theirs.txt @@ -0,0 +1,3 @@ +include/trace/hooks/dtask.h +include/trace/hooks/sched.h +kernel/sched/vendor_hooks.c From cb4a86b9b3d4f491f9496bb1c6f836affe332adf Mon Sep 17 00:00:00 2001 From: Axymorrsen Date: Wed, 29 Jul 2026 16:07:39 +0800 Subject: [PATCH 3/6] sync: add per-block conflict resolution planner --- scripts/resolve_merge_plan.py | 129 ++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 scripts/resolve_merge_plan.py diff --git a/scripts/resolve_merge_plan.py b/scripts/resolve_merge_plan.py new file mode 100644 index 000000000000..62d77b332913 --- /dev/null +++ b/scripts/resolve_merge_plan.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +"""Resolve conflict blocks according to an auditable per-file JSON plan.""" + +from __future__ import annotations + +import argparse +import json +import os +import sys +from pathlib import Path +from typing import Any + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + parser.add_argument("--root", type=Path, default=Path.cwd()) + parser.add_argument("--plan", type=Path, required=True) + return parser.parse_args() + + +def atomic_write(path: Path, data: bytes) -> None: + temporary = path.with_name(f".{path.name}.plan-tmp") + temporary.write_bytes(data) + os.chmod(temporary, path.stat().st_mode) + temporary.replace(path) + + +def parse_blocks(data: bytes, repo_path: str) -> tuple[list[bytes], list[tuple[list[bytes], list[bytes]]]]: + lines = data.splitlines(keepends=True) + segments: list[bytes] = [] + blocks: list[tuple[list[bytes], list[bytes]]] = [] + plain: list[bytes] = [] + index = 0 + + while index < len(lines): + line = lines[index] + if not line.startswith(b"<<<<<<<"): + plain.append(line) + index += 1 + continue + + segments.append(b"".join(plain)) + plain = [] + index += 1 + ours: list[bytes] = [] + while index < len(lines) and not lines[index].startswith(b"======="): + if lines[index].startswith((b"<<<<<<<", b">>>>>>>")): + raise ValueError(f"nested or malformed conflict in {repo_path}") + ours.append(lines[index]) + index += 1 + if index >= len(lines): + raise ValueError(f"missing ======= marker in {repo_path}") + + index += 1 + theirs: list[bytes] = [] + while index < len(lines) and not lines[index].startswith(b">>>>>>>"): + if lines[index].startswith((b"<<<<<<<", b"=======")): + raise ValueError(f"nested or malformed conflict in {repo_path}") + theirs.append(lines[index]) + index += 1 + if index >= len(lines): + raise ValueError(f"missing >>>>>>> marker in {repo_path}") + index += 1 + blocks.append((ours, theirs)) + + segments.append(b"".join(plain)) + return segments, blocks + + +def replacement(decision: Any, ours: list[bytes], theirs: list[bytes], repo_path: str, block_no: int) -> bytes: + if decision == "ours": + return b"".join(ours) + if decision == "theirs": + return b"".join(theirs) + if isinstance(decision, dict) and set(decision) == {"custom"}: + text = decision["custom"] + if not isinstance(text, str): + raise TypeError(f"custom replacement must be text: {repo_path} block {block_no}") + return text.encode("utf-8") + raise ValueError(f"invalid decision for {repo_path} block {block_no}: {decision!r}") + + +def main() -> int: + args = parse_args() + root = args.root.resolve() + plan = json.loads(args.plan.read_text(encoding="utf-8")) + if not isinstance(plan, dict) or not plan: + raise ValueError("plan must be a non-empty object") + + total_blocks = 0 + for repo_path, decisions in plan.items(): + if not isinstance(repo_path, str) or repo_path.startswith("/") or ".." in Path(repo_path).parts: + raise ValueError(f"unsafe repository path: {repo_path!r}") + if not isinstance(decisions, list): + raise TypeError(f"decisions must be a list: {repo_path}") + + target = (root / repo_path).resolve() + target.relative_to(root) + if not target.is_file(): + raise FileNotFoundError(f"conflicted file not found: {repo_path}") + + segments, blocks = parse_blocks(target.read_bytes(), repo_path) + if len(blocks) != len(decisions): + raise ValueError( + f"block count mismatch for {repo_path}: file has {len(blocks)}, plan has {len(decisions)}" + ) + + output: list[bytes] = [segments[0]] + for index, ((ours, theirs), decision) in enumerate(zip(blocks, decisions, strict=True), start=1): + output.append(replacement(decision, ours, theirs, repo_path, index)) + output.append(segments[index]) + + resolved = b"".join(output) + if any(line.startswith((b"<<<<<<<", b"=======", b">>>>>>>")) for line in resolved.splitlines()): + raise ValueError(f"unresolved marker remains in {repo_path}") + atomic_write(target, resolved) + total_blocks += len(blocks) + print(f"resolved {len(blocks):2d} planned block(s): {repo_path}") + + print(f"resolved {total_blocks} planned conflict block(s) across {len(plan)} file(s)") + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except Exception as exc: # noqa: BLE001 + print(f"error: {exc}", file=sys.stderr) + raise SystemExit(1) From ddfb0082c3fe23133aaa04685b1a0f5e517d182b Mon Sep 17 00:00:00 2001 From: Axymorrsen Date: Wed, 29 Jul 2026 16:08:17 +0800 Subject: [PATCH 4/6] sync: add per-block scheduler resolution plan --- .../16.0.9.401/pass4-scheduler-plan.json | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 sync/resolutions/16.0.9.401/pass4-scheduler-plan.json diff --git a/sync/resolutions/16.0.9.401/pass4-scheduler-plan.json b/sync/resolutions/16.0.9.401/pass4-scheduler-plan.json new file mode 100644 index 000000000000..0097438bb225 --- /dev/null +++ b/sync/resolutions/16.0.9.401/pass4-scheduler-plan.json @@ -0,0 +1,28 @@ +{ + "kernel/fork.c": [ + "ours", + "ours", + { + "custom": "#ifdef CONFIG_HMBIRD_SCHED\n\thmbird_free(tsk);\n#endif\n\ttrace_android_vh_put_task(tsk);\n" + }, + "ours", + "ours" + ], + "kernel/sched/build_policy.c": [ + "theirs", + "ours" + ], + "kernel/sched/fair.c": [ + "ours", + "theirs" + ], + "kernel/sched/sched.h": [ + { + "custom": "static inline int normal_policy(int policy)\n{\n#ifdef CONFIG_HMBIRD_SCHED\n\tif (policy == SCHED_HMBIRD)\n\t\treturn true;\n#endif\n\treturn policy == SCHED_NORMAL;\n}\n\nstatic inline int fair_policy(int policy)\n{\n\treturn normal_policy(policy) || policy == SCHED_BATCH;\n" + }, + "theirs", + "theirs", + "theirs", + "ours" + ] +} From 3f094bfc3e48bf11da1dd7d8dffa1c5d16f85d24 Mon Sep 17 00:00:00 2001 From: Axymorrsen Date: Wed, 29 Jul 2026 16:09:20 +0800 Subject: [PATCH 5/6] ci: test OnePlus 13 scheduler resolution pass 4 --- ...st-upstream-resolution-pass4-scheduler.yml | 207 ++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 .github/workflows/test-upstream-resolution-pass4-scheduler.yml diff --git a/.github/workflows/test-upstream-resolution-pass4-scheduler.yml b/.github/workflows/test-upstream-resolution-pass4-scheduler.yml new file mode 100644 index 000000000000..981e882474a1 --- /dev/null +++ b/.github/workflows/test-upstream-resolution-pass4-scheduler.yml @@ -0,0 +1,207 @@ +name: Test OnePlus 13 Scheduler Resolution Pass 4 + +on: + pull_request: + branches: ["6.6-final"] + paths: + - "sync/resolutions/16.0.9.401/pass4-scheduler-*" + - "scripts/resolve_merge_plan.py" + - ".github/workflows/test-upstream-resolution-pass4-scheduler.yml" + +permissions: + contents: read + +concurrency: + group: oneplus13-test-upstream-resolution-pass4-scheduler + cancel-in-progress: true + +env: + LOCAL_BRANCH: 6.6-final + OFFICIAL_REPO: https://github.com/OnePlusOSS/android_kernel_common_oneplus_sm8750.git + OFFICIAL_SHA: e1b346b6b4f4096eb342ae3684838a942fd6f6c4 + EXPECTED_INITIAL_CONFLICTS: "116" + EXPECTED_AFTER_PASS1: "85" + EXPECTED_AFTER_PASS2: "67" + EXPECTED_AFTER_PASS3: "50" + EXPECTED_AFTER_PASS4: "33" + +jobs: + test-pass-4-scheduler: + if: github.repository == 'Zhanfg/android_kernel_common_oneplus_sm8750' + runs-on: ubuntu-24.04 + timeout-minutes: 30 + + steps: + - name: Fetch base, resolution inputs and official history + env: + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + shell: bash + run: | + set -euo pipefail + git init . + git config gc.auto 0 + git config advice.detachedHead false + git remote add local "https://github.com/${GITHUB_REPOSITORY}.git" + git remote add official "${OFFICIAL_REPO}" + git fetch --filter=blob:none --no-tags --no-recurse-submodules local \ + "+refs/heads/${LOCAL_BRANCH}:refs/remotes/local/${LOCAL_BRANCH}" \ + "+${PR_HEAD_SHA}:refs/remotes/local/resolution-head" + git fetch --filter=blob:none --no-tags --no-recurse-submodules official \ + "+${OFFICIAL_SHA}:refs/remotes/official/pinned" + git checkout --detach "refs/remotes/local/${LOCAL_BRANCH}" + + for path in \ + scripts/resolve_merge_markers.py \ + scripts/resolve_merge_plan.py \ + sync/resolutions/16.0.9.401/pass1-ours.txt \ + sync/resolutions/16.0.9.401/pass2-ours.txt \ + sync/resolutions/16.0.9.401/pass2-theirs.txt \ + sync/resolutions/16.0.9.401/pass3-ours.txt \ + sync/resolutions/16.0.9.401/pass3-theirs.txt \ + sync/resolutions/16.0.9.401/pass4-scheduler-ours.txt \ + sync/resolutions/16.0.9.401/pass4-scheduler-theirs.txt \ + sync/resolutions/16.0.9.401/pass4-scheduler-plan.json; do + git show "refs/remotes/local/resolution-head:${path}" > "/tmp/$(basename "${path}")" + done + chmod +x /tmp/resolve_merge_markers.py /tmp/resolve_merge_plan.py + python3 -m py_compile /tmp/resolve_merge_markers.py /tmp/resolve_merge_plan.py + + - name: Apply and verify passes 1-4 + id: resolve + shell: bash + run: | + set -euo pipefail + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + count_conflicts() { + git diff --name-only --diff-filter=U | LC_ALL=C sort -u > "$1" + awk 'END {print NR + 0}' "$1" + } + + stage_paths() { + while IFS= read -r path; do + [ -n "${path}" ] || continue + git add -- "${path}" + done < "$1" + } + + verify_paths() { + local list="$1" + local remaining="$2" + while IFS= read -r path; do + [ -n "${path}" ] || continue + if grep -Fxq -- "${path}" "${remaining}"; then + echo "Resolved path remains unmerged: ${path}" >&2 + exit 1 + fi + if grep -n -E '^(<<<<<<<|=======|>>>>>>>)' -- "${path}"; then + echo "Conflict marker remains: ${path}" >&2 + exit 1 + fi + done < "${list}" + } + + apply_markers() { + local list="$1" + local choice="$2" + local log="$3" + python3 /tmp/resolve_merge_markers.py --root . --paths-file "${list}" --choice "${choice}" | tee "${log}" + stage_paths "${list}" + } + + set +e + git merge --no-commit --no-ff "${OFFICIAL_SHA}" + MERGE_STATUS=$? + set -e + BEFORE="$(count_conflicts /tmp/conflicts-before.txt)" + if [ "${MERGE_STATUS}" -eq 0 ] || [ "${BEFORE}" != "${EXPECTED_INITIAL_CONFLICTS}" ]; then + echo "Initial conflict baseline mismatch: expected ${EXPECTED_INITIAL_CONFLICTS}, got ${BEFORE}." >&2 + exit 1 + fi + + apply_markers /tmp/pass1-ours.txt ours /tmp/pass1.log + AFTER1="$(count_conflicts /tmp/conflicts-after-pass1.txt)" + verify_paths /tmp/pass1-ours.txt /tmp/conflicts-after-pass1.txt + [ "${AFTER1}" = "${EXPECTED_AFTER_PASS1}" ] || { echo "Expected ${EXPECTED_AFTER_PASS1} after pass 1, got ${AFTER1}." >&2; exit 1; } + + apply_markers /tmp/pass2-ours.txt ours /tmp/pass2-ours.log + apply_markers /tmp/pass2-theirs.txt theirs /tmp/pass2-theirs.log + AFTER2="$(count_conflicts /tmp/conflicts-after-pass2.txt)" + verify_paths /tmp/pass2-ours.txt /tmp/conflicts-after-pass2.txt + verify_paths /tmp/pass2-theirs.txt /tmp/conflicts-after-pass2.txt + [ "${AFTER2}" = "${EXPECTED_AFTER_PASS2}" ] || { echo "Expected ${EXPECTED_AFTER_PASS2} after pass 2, got ${AFTER2}." >&2; exit 1; } + + apply_markers /tmp/pass3-ours.txt ours /tmp/pass3-ours.log + apply_markers /tmp/pass3-theirs.txt theirs /tmp/pass3-theirs.log + AFTER3="$(count_conflicts /tmp/conflicts-after-pass3.txt)" + verify_paths /tmp/pass3-ours.txt /tmp/conflicts-after-pass3.txt + verify_paths /tmp/pass3-theirs.txt /tmp/conflicts-after-pass3.txt + [ "${AFTER3}" = "${EXPECTED_AFTER_PASS3}" ] || { echo "Expected ${EXPECTED_AFTER_PASS3} after pass 3, got ${AFTER3}." >&2; exit 1; } + + apply_markers /tmp/pass4-scheduler-ours.txt ours /tmp/pass4-ours.log + apply_markers /tmp/pass4-scheduler-theirs.txt theirs /tmp/pass4-theirs.log + python3 /tmp/resolve_merge_plan.py --root . --plan /tmp/pass4-scheduler-plan.json | tee /tmp/pass4-plan.log + python3 - <<'PY' + import json + from pathlib import Path + for path in json.loads(Path('/tmp/pass4-scheduler-plan.json').read_text()): + print(path) + PY + python3 - <<'PY' > /tmp/pass4-plan-paths.txt + import json + from pathlib import Path + for path in json.loads(Path('/tmp/pass4-scheduler-plan.json').read_text()): + print(path) + PY + stage_paths /tmp/pass4-plan-paths.txt + + AFTER4="$(count_conflicts /tmp/conflicts-after-pass4.txt)" + verify_paths /tmp/pass4-scheduler-ours.txt /tmp/conflicts-after-pass4.txt + verify_paths /tmp/pass4-scheduler-theirs.txt /tmp/conflicts-after-pass4.txt + verify_paths /tmp/pass4-plan-paths.txt /tmp/conflicts-after-pass4.txt + [ "${AFTER4}" = "${EXPECTED_AFTER_PASS4}" ] || { echo "Expected ${EXPECTED_AFTER_PASS4} after pass 4, got ${AFTER4}." >&2; exit 1; } + + grep -qx 'CONFIG_HMBIRD_SCHED=y' arch/arm64/configs/gki_defconfig + ! grep -qx 'CONFIG_SCHED_CLASS_EXT=y' arch/arm64/configs/gki_defconfig + ! grep -qx 'CONFIG_SLIM_SCHED=y' arch/arm64/configs/gki_defconfig + grep -q '^#define SCHED_HMBIRD[[:space:]]*7$' include/uapi/linux/sched.h + ! grep -q '^#define SCHED_EXT[[:space:]]*7$' include/uapi/linux/sched.h + grep -q 'trace_android_vh_put_task(tsk);' kernel/fork.c + grep -q 'EXPORT_TRACEPOINT_SYMBOL_GPL(android_rvh_task_fits_cpu);' kernel/sched/vendor_hooks.c + grep -q 'EXPORT_SYMBOL_GPL(sysctl_sched_base_slice);' kernel/sched/fair.c + grep -q 'min(cost, sysctl_sched_migration_cost + 200)' kernel/sched/fair.c + grep -q 'sched_weight_from_cgroup' kernel/sched/sched.h + + { + echo "# OnePlus 13 scheduler resolution pass 4" + echo + echo "- Initial conflicts: ${BEFORE}" + echo "- After pass 1: ${AFTER1}" + echo "- After pass 2: ${AFTER2}" + echo "- After pass 3: ${AFTER3}" + echo "- Scheduler paths resolved: $((AFTER3 - AFTER4))" + echo "- Remaining conflicts: ${AFTER4}" + echo "- Architecture: fair/wait + HMBIRD; official SCX/SLIM disabled" + echo "- Rule validation: passed" + echo "- Source integration: still blocked" + } > /tmp/pass4-report.md + cat /tmp/pass4-report.md >> "${GITHUB_STEP_SUMMARY}" + git merge --abort 2>/dev/null || true + + - name: Upload scheduler pass evidence + uses: actions/upload-artifact@v4 + with: + name: oneplus13-common-resolution-pass4-scheduler-${{ github.run_id }} + path: | + /tmp/pass4-report.md + /tmp/pass4-ours.log + /tmp/pass4-theirs.log + /tmp/pass4-plan.log + /tmp/conflicts-after-pass4.txt + if-no-files-found: error + retention-days: 30 + + - name: Confirm integration remains blocked + run: | + echo "Scheduler pass rules are reproducible. The source merge remains blocked by 33 unresolved paths." From 50f03b91d96bae48ed66b9e71ea4d2276e13dc8d Mon Sep 17 00:00:00 2001 From: Axymorrsen Date: Wed, 29 Jul 2026 16:10:39 +0800 Subject: [PATCH 6/6] sync: document scheduler resolution pass 4 --- .../resolutions/16.0.9.401/PASS4_SCHEDULER.md | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 sync/resolutions/16.0.9.401/PASS4_SCHEDULER.md diff --git a/sync/resolutions/16.0.9.401/PASS4_SCHEDULER.md b/sync/resolutions/16.0.9.401/PASS4_SCHEDULER.md new file mode 100644 index 000000000000..16542edec78e --- /dev/null +++ b/sync/resolutions/16.0.9.401/PASS4_SCHEDULER.md @@ -0,0 +1,73 @@ +# Resolution pass 4: scheduler architecture + +Pass 4 applies after the validated pass-1, pass-2 and pass-3 rules. + +Expected cumulative result: + +```text +116 initial conflicts +85 after pass 1 +67 after pass 2 +50 after pass 3 +17 scheduler paths resolved by pass 4 +33 remaining conflicts +``` + +## Selected architecture + +```text +Default scheduler path: fair / wait +Optional custom path: HMBIRD / 风驰 +Official CONFIG_SCHED_CLASS_EXT / SCX: disabled +Official CONFIG_SLIM_SCHED: disabled +``` + +The official and custom implementations both assign scheduler policy number `7` +and modify the same fork, tick, class-selection, cgroup and idle interfaces. +They cannot be enabled together without a separate ABI and scheduler-class +redesign. + +## Uniform local paths + +`pass4-scheduler-ours.txt` retains HMBIRD-specific conflict blocks in: + +- GKI config and scheduler-class linker ordering +- task and scheduler state definitions +- user-visible policy number +- HMBIRD scheduler class, debug, idle and tick hooks +- the 28 conflict blocks in `kernel/sched/core.c` + +Clean official hunks outside conflict blocks remain applied. + +## Uniform official hook paths + +`pass4-scheduler-theirs.txt` imports official Android trace hook declarations and +exports which are independent from enabling SCX. + +## Per-block combined files + +`pass4-scheduler-plan.json` handles four mixed files: + +- `kernel/fork.c`: keep HMBIRD task cleanup and add the official + `trace_android_vh_put_task()` hook. +- `kernel/sched/build_policy.c`: keep HMBIRD source composition while accepting + unconditional helper includes. +- `kernel/sched/fair.c`: retain BORE/base-slice behavior and accept the official + bounded new-idle cost update. +- `kernel/sched/sched.h`: add the official generic cgroup weight helpers, retain + HMBIRD policy handling, and keep HMBIRD scheduler declarations. + +## Validation + +The Actions workflow verifies: + +- cumulative conflict counts through all four passes; +- no marker or unmerged index remains in the 17 scheduler paths; +- HMBIRD is enabled in `gki_defconfig`; +- official SCX and SLIM are not enabled; +- only `SCHED_HMBIRD=7` remains in the UAPI policy definitions; +- official task release and task-fits-CPU hooks are retained; +- BORE base-slice export, bounded new-idle cost and cgroup weight helpers exist. + +This pass does not create a source merge commit. The remaining 33 conflicts and +full OKI validation continue to block integration.