diff --git a/mseries/OS9-HANDOFF.md b/mseries/OS9-HANDOFF.md index 855f2ee9..743fbbc1 100644 --- a/mseries/OS9-HANDOFF.md +++ b/mseries/OS9-HANDOFF.md @@ -1,5 +1,11 @@ ## Warren local AI and eGPU updates — 2026-10-30 +Installed `mseries-nvidia-open-source` `595.112.07+apple.1-1` (SHA-256 `a397b68a...b209ab23`) containing the three prepared NVIDIA fixes and license at `/usr/src/mseries-nvidia-open-595.112.07`. This is source only; no NVIDIA module or matching CUDA userspace is installed. The [source package script](nvidia-egpu/package-source.sh) verifies the exact prepared diff before packaging. + +ConnectX kernel CI run 41049918349 passed, all artifact checksums passed, and package `b18e89d1...21601b5` is installed beside wip5; `mlx5_core` and `mlxfw` match its kernel release. No ConnectX boot entry was staged because `/boot` has ~45 MiB free; no adapter is attached. Combined revision 2 includes AMDGPU, Nouveau and ConnectX; native build run 41059424955 is running. The prior revision-1 build was cancelled to replace its Nouveau-disabled configuration. + +Installed `mseries-tensorfold-vulkan` 0.1+git.b3ab9ba-1, with an opt-in private-Honeykrisp launcher. Installed binary matches the tested source build; guarded Qwen 2B loopback smoke passed. MLX remains the default service. The combined kernel revision 2 enables AMDGPU, Nouveau and ConnectX, and NVIDIA graphics firmware is installed. NVIDIA-open 595 source preparation includes upstream 16K PRs #1480/#1481 and a 595 rebase of #1482; its 29 additions match upstream, with the 615 original retained as reference. Kernel headers and matching NVIDIA userspace are not supplied. [TensorFold](local-ai/receipts/2026-10-30-tensorfold-package/README.md), [NVIDIA kit](nvidia-egpu/README.md). + Added `unified72-combined`, a distinct downstream kernel profile carrying #188 plus both AMDGPU and ConnectX Ethernet modules so one kernel can provide both. Source preparation, 12 PMP host tests, 88 Aurora tests and GCC olddefconfig passed. Native build pending. Existing eGPU trial remains staged; `/boot` has about 45 MiB free, insufficient for another Image plus initramfs without a storage plan. [Details](aurora/unified72/README.md#combined-egpu-and-connectx-package--october-30-2026). Owner clarified that upstream acceptance is not ours to duplicate; downstream regression testing and packaging are requested. Deployed official MLX v0.7.35 wheel (`39671c8a...19fba82`) to the live venv with the previous venv retained at `~/.local/share/mlx-omarchy/venv.before-v0.7.35-20261030`. Qwen 2B baseline/work120k gate passed; Granite 1B short text matched earlier baseline; reply service restarted and returned `ready`, expected private ICD and server patches checked. Installer/source pins now target `42b30a2d`. [Receipt](local-ai/receipts/2026-10-30-mlx-v0735-deploy/README.md). diff --git a/mseries/ROADMAP.md b/mseries/ROADMAP.md index 32d10102..e3dbed33 100644 --- a/mseries/ROADMAP.md +++ b/mseries/ROADMAP.md @@ -5644,6 +5644,6 @@ Keep the unified T3 Wingpanel menu and use the Dock launcher to open the T3 wind The separate Aurora `unified72-egpu` profile carries upstream #188 with AMDGPU enabled. Its CI-built package and AMD firmware are installed, and a non-default trial entry is staged; running wip5 and shared boot DTB remain in place. J314S dock, recovery, matched-DTB and actual eGPU testing remain. See [candidate status](aurora/unified72/README.md#separate-egpu-candidate--october-30-2026). -The separate `unified72-connectx` profile enables the Mellanox Ethernet module on wip5 source; it requires a native build and real-adapter link and throughput qualification. [Status](aurora/unified72/README.md#separate-connectx-candidate--october-30-2026). +The separate `unified72-connectx` profile enables the Mellanox Ethernet module on wip5 source; its native package is installed, while boot and real-adapter link and throughput tests remain. [Status](aurora/unified72/README.md#separate-connectx-candidate--october-30-2026). -The `unified72-combined` profile packages both driver sets in one kernel; native build, boot-space planning and device regression tests remain. [Status](aurora/unified72/README.md#combined-egpu-and-connectx-package--october-30-2026). +The `unified72-combined` profile packages AMDGPU, Nouveau and ConnectX in one kernel; native build, boot-space planning and device regression tests remain. [Status](aurora/unified72/README.md#combined-egpu-and-connectx-package--october-30-2026). diff --git a/mseries/aurora/test_qualification.py b/mseries/aurora/test_qualification.py index 8fbe3f2e..2bc952fc 100644 --- a/mseries/aurora/test_qualification.py +++ b/mseries/aurora/test_qualification.py @@ -229,7 +229,7 @@ def test_unified72_combines_egpu_and_connectx_without_overwriting_either(self): self.assertEqual(config["MLX5_CORE"], "m") self.assertEqual(config["MLX5_CORE_EN"], "y") self.assertEqual(config["MLXFW"], "m") - self.assertEqual(config["DRM_NOUVEAU"], "n") + self.assertEqual(config["DRM_NOUVEAU"], "m") self.assertEqual([p["sha256"] for p in data["patches"]], [p["sha256"] for p in egpu["patches"]]) diff --git a/mseries/aurora/unified72/README.md b/mseries/aurora/unified72/README.md index c2ba82e6..63413766 100644 --- a/mseries/aurora/unified72/README.md +++ b/mseries/aurora/unified72/README.md @@ -2,13 +2,13 @@ ## Combined eGPU and ConnectX package — October 30, 2026 -`unified72-combined` builds one distinct kernel from the #188 merge and the wip5 display patch series, with both `DRM_AMDGPU=m` and ConnectX Ethernet (`MLX5_CORE=m`, `MLX5_CORE_EN=y`, `MLXFW=m`). RDMA and Nouveau are not enabled by this profile. The original individual profiles remain available and do not share its package or kernel identity. Source preparation and the 12 PMP host tests passed, all 88 Aurora unit tests passed, and a local GCC `olddefconfig` retained both driver sets and 16K pages. The native [package build](https://github.com/techmore/ElementaryOS_MSeries/actions/runs/41057113100) was submitted at `f70f5cc8`; hardware checks are separate. The existing eGPU trial stays staged; only about 45 MiB remains on `/boot`, so adding another boot entry requires a storage plan rather than removing existing recovery files. +Revision 2 of `unified72-combined` builds one distinct kernel from the #188 merge and wip5 display patches with AMDGPU, Nouveau and ConnectX Ethernet modules. RDMA is not enabled. A GCC `olddefconfig` retained `DRM_AMDGPU=m`, `DRM_NOUVEAU=m`, `MLX5_CORE=m`, `MLX5_CORE_EN=y`, `MLXFW=m` and 16K pages; all 88 Aurora unit tests pass. Revision 1 CI run 41057113100 was cancelled because it omitted Nouveau; the revision 2 [native build](https://github.com/techmore/ElementaryOS_MSeries/actions/runs/41059424955) is running at `b3a3e9cb`. The staged eGPU trial and recovery entries remain intact, and about 45 MiB is free on `/boot`. The separately packaged NVIDIA-open/CUDA source kit is documented in [NVIDIA eGPU](../../nvidia-egpu/README.md). ## Separate ConnectX candidate — October 30, 2026 The running wip5 kernel has `NET_VENDOR_MELLANOX` disabled. `connectx-series.json` defines an independent `unified72-connectx` candidate on the same wip5 source and five display patches, with `NET_VENDOR_MELLANOX=y`, `MLX5_CORE=m`, `MLX5_CORE_EN=y` and `MLXFW=m`. AMDGPU remains off; RDMA is not enabled by this profile. It has its own package and kernel identity; it does not replace the wip5 or eGPU profiles. The author reports that a ConnectX-5 Ex enumerated on M2 Max without special PCI or DT changes; that result is not a J314S or network throughput qualification ([setup](https://github.com/joshuaswarren/omarchy-egpu/blob/main/cards/connectx-5/README.md)). -Pinned source verification, patch preparation and 12 PMP host tests passed. A GCC `olddefconfig` retained all four ConnectX settings and 16K pages. The native LLVM kernel and module build, boot, an attached adapter, link stability and throughput remain untested. No ConnectX card appeared in this machine's PCI inventory. The Aurora workflow's manual `unified72-connectx` profile was submitted as [CI run 41049918349](https://github.com/techmore/ElementaryOS_MSeries/actions/runs/41049918349), queued behind the eGPU build. Do not promote it from configuration checks alone. +Pinned source verification, patch preparation and 12 PMP host tests passed. A GCC `olddefconfig` retained all four ConnectX settings and 16K pages. The native LLVM kernel and module build passed in CI; boot, an attached adapter, link stability and throughput remain untested. No ConnectX card appeared in this machine's PCI inventory. The [CI run 41049918349](https://github.com/techmore/ElementaryOS_MSeries/actions/runs/41049918349) passed. Its internal SHA256SUMS passed; package SHA-256 `b18e89d1552b6614bb554a348a4488162e677c72c9bd13ff9b7325f7d21601b5` was installed as `linux-image-elementary-aurora72-connectx-test` alongside wip5. The `mlx5_core` and `mlxfw` modules are present with the matching kernel release; no boot entry was staged due to limited boot space. The running kernel is still wip5. An additional local GCC compile of `mlx5_core` and `mlxfw` completed after `modules_prepare`. It used `KBUILD_MODPOST_WARN=1` without a full-kernel `Module.symvers`, so modpost reported unresolved symbols and skipped BTF: this proves source compilation only, not link or load compatibility. These private build outputs must not be installed or loaded. The diagnostic build wrote into the prepared source directory, invalidating its recorded source-tree digest; create a fresh prepared tree for any subsequent `candidate.py check` or full build. Logs remain in `~/.cache/mseries-connectx-20261030/`. diff --git a/mseries/aurora/unified72/combined-series.json b/mseries/aurora/unified72/combined-series.json index 2cadbe48..e93cc2b1 100644 --- a/mseries/aurora/unified72/combined-series.json +++ b/mseries/aurora/unified72/combined-series.json @@ -1,7 +1,7 @@ { "schema": 1, "base_source": "c46d7dcdb8712ea9aeb0ce47b439ffae76371e55", - "revision": 1, + "revision": 2, "source": { "repository": "omacom/linux-aurora", "commit": "c506e1c3c1d7808eddc251e3029b72e9f17e00df", @@ -15,10 +15,10 @@ "sha256": "13cfc68aa95ffd0ac858e1a57d95691a10499a4800f3a8ae6deefc7394f238e2" }, "kernel_release": { - "unified72-combined": "7.2.11-elementary-aurora72-combined-test" + "unified72-combined": "7.2.11-elementary-aurora72-combined2-test" }, - "debian_package": "linux-image-elementary-aurora72-combined-test", - "debian_version": "7.2.11+elementary.aurora72.combined.test-1", + "debian_package": "linux-image-elementary-aurora72-combined2-test", + "debian_version": "7.2.11+elementary.aurora72.combined2.test-1", "patches": [ { "file": "0001-drm-apple-srgb-external-display-colour.patch", @@ -72,14 +72,15 @@ "NET_VENDOR_MELLANOX": "y", "MLX5_CORE": "m", "MLX5_CORE_EN": "y", - "MLXFW": "m" + "MLXFW": "m", + "DRM_NOUVEAU": "m" } }, "host_tests": [ "tools/asahi/pmp-bootargs-tests.rs", "tools/asahi/pmp-power-tests.rs" ], - "qualification": "Combined downstream candidate: Aurora #188 merge and wip5 display patches, AMDGPU and ConnectX Ethernet modules enabled. Build, boot, shared-DTB fallback, dock and adapter testing remain separate.", + "qualification": "Combined downstream candidate: Aurora #188 merge and wip5 display patches, AMDGPU, Nouveau and ConnectX Ethernet modules enabled. Build, boot, shared-DTB fallback, dock and adapter testing remain separate.", "superseded_patches": [ { "file": "0001-stock-bindgen-layout-policy.patch", diff --git a/mseries/local-ai/README.md b/mseries/local-ai/README.md index 8a35e6c3..4c1604a7 100644 --- a/mseries/local-ai/README.md +++ b/mseries/local-ai/README.md @@ -2,6 +2,8 @@ The installed desktop uses private Linux MLX v0.7.35, Newelle for desktop and personal assistance, and a separate 16K coding profile for Crush. Qwen3.5 4B is the measured default for the 32 GB M1 Pro; Qwen9 is an optional larger preset. [Measured results, methods and limits](receipts/2026-10-16.md) explain the provisional model choices. [Exact revisions and licenses](receipts/2026-10-16/source-pins.json) accompany the synthetic raw results. +TensorFold Vulkan is available as an opt-in Debian package and launcher (`mseries-tensorfold`); see the [package and regression receipt](receipts/2026-10-30-tensorfold-package/README.md). The existing MLX service remains available. + ## Install and launch ```sh diff --git a/mseries/local-ai/engine-compare.py b/mseries/local-ai/engine-compare.py index 8608df4a..3335b2c1 100755 --- a/mseries/local-ai/engine-compare.py +++ b/mseries/local-ai/engine-compare.py @@ -59,8 +59,8 @@ DRIVER = (ICD.parent / 'mesa-git-sha').read_text().strip() if (ICD.parent / 'mesa-git-sha').exists() else 'unknown' RAPID_PATH = f"{HOME}/.cache/mseries-metal2vk/rapid-python:{HOME}/.cache/mseries-metal2vk/rapid-src" RAPID_REV = '1d09cdb684bbf58fac130f5804b08ee2e1f56558' -TF_BIN = HOME / '.cache/mseries-tensorfold/src/zig-out/native/bin/tensorfold-native-vulkan' -TF_REV = '92bb49db5c313c105cbf4b44638d1e1896615174' # PR 1544; receipts record the rev they ran +TF_BIN = Path('/usr/libexec/mseries/tensorfold-native-vulkan') +TF_REV = 'b3ab9ba217a80c63fcc48f7643ddfe339a8f4bdc' # PR 1544; receipts record the rev they ran PROMPT = 'Explain in plain language how a rainbow forms. Give four distinct sentences.' LONG_PROMPT = ('Explain in detail how a rainbow forms, including refraction, reflection, color ' 'and viewing angle. Write at least 350 words.') @@ -384,7 +384,7 @@ def main(): print(f'skipping omlx-120k: {OMLX_BIN} missing (omarchy-mlx packaging/omlx-linux/install.sh)', file=sys.stderr) arms.remove('omlx-120k') if 'tensorfold' in arms and not TF_BIN.exists(): - raise SystemExit('TensorFold server missing: run tensorfold-vulkan.sh build') + raise SystemExit('TensorFold server missing: build and install the mseries-tensorfold-vulkan package') out = a.output or Path(__file__).resolve().parent / 'receipts' / ( f"{time.strftime('%Y-%m-%d')}-engine-compare-{a.model}-{'long' if a.long else 'short'}" + ('' if DRIVER == '7faf04c065' else f'-hk{DRIVER}') + (f'-c{CONCURRENCY}' if CONCURRENCY > 1 else '') diff --git a/mseries/local-ai/package-tensorfold-vulkan.sh b/mseries/local-ai/package-tensorfold-vulkan.sh new file mode 100755 index 00000000..44bfc375 --- /dev/null +++ b/mseries/local-ai/package-tensorfold-vulkan.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# Package the previously built, pinned TensorFold Vulkan server without boot hooks. +set -euo pipefail +here=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) +root=${MSERIES_TENSORFOLD_ROOT:-$HOME/.cache/mseries-tensorfold} +src=$root/src +pin=b3ab9ba217a80c63fcc48f7643ddfe339a8f4bdc +binary=$src/zig-out/native/bin/tensorfold-native-vulkan +[[ $(git -C "$src" rev-parse HEAD) == "$pin" ]] || { echo 'Unexpected TensorFold revision' >&2; exit 1; } +[[ -z $(git -C "$src" status --porcelain --untracked-files=no) ]] || { echo 'TensorFold source has tracked edits' >&2; exit 1; } +[[ -x $binary ]] || { echo 'Build the TensorFold server first' >&2; exit 1; } +out=${1:?usage: package-tensorfold-vulkan.sh NEW_OUTPUT_DEB} +[[ ! -e $out ]] || { echo 'Refusing to overwrite output' >&2; exit 1; } +work=$(mktemp -d "$root/package.XXXXXXXX") +trap 'rm -rf "$work"' EXIT +mkdir -p "$work/DEBIAN" "$work/usr/bin" "$work/usr/libexec/mseries" "$work/usr/share/doc/mseries-tensorfold-vulkan" +install -m755 "$binary" "$work/usr/libexec/mseries/tensorfold-native-vulkan" +install -m755 "$here/tensorfold-launcher.sh" "$work/usr/bin/mseries-tensorfold" +cp "$src/LICENSE" "$work/usr/share/doc/mseries-tensorfold-vulkan/copyright" +cp -a "$src/LICENSES" "$work/usr/share/doc/mseries-tensorfold-vulkan/" +printf '%s\n' "$pin" > "$work/usr/share/doc/mseries-tensorfold-vulkan/source-revision" +cat > "$work/DEBIAN/control" <<'CONTROL' +Package: mseries-tensorfold-vulkan +Version: 0.1+git.b3ab9ba-1 +Architecture: arm64 +Maintainer: ElementaryOS MSeries contributors +Depends: libc6, libvulkan1 +Section: utils +Priority: optional +Description: TensorFold Vulkan local inference engine for Apple Silicon + Opt-in Qwen3.5-2B Vulkan engine, built from upstream PR 1544. +CONTROL +dpkg-deb --root-owner-group --build "$work" "$out" diff --git a/mseries/local-ai/receipts/2026-10-30-tensorfold-package/README.md b/mseries/local-ai/receipts/2026-10-30-tensorfold-package/README.md new file mode 100644 index 00000000..1f8de722 --- /dev/null +++ b/mseries/local-ai/receipts/2026-10-30-tensorfold-package/README.md @@ -0,0 +1,7 @@ +# TensorFold Vulkan Debian package — 2026-10-30 + +Packaged and installed the upstream PR 1544 source revision `b3ab9ba217a80c63fcc48f7643ddfe339a8f4bdc` as `mseries-tensorfold-vulkan` version `0.1+git.b3ab9ba-1`. The .deb SHA-256 is `aff78db1e028d4d80a8f5dbd3abd25469e6d8ae492d9add4113e2602d8fd21f2`; the installed engine SHA-256 `eae8c9f955f39d2be33af57cbfe254225fb2a8a349d577b749743e174e89511b` matches the tested build. The package retains upstream licenses, has no maintainer scripts, and installs `mseries-tensorfold`, which selects the private Honeykrisp ICD for its own process. + +The installed engine passed a guarded loopback Qwen3.5-2B 32-token serving smoke and produced the prior expected text hash; [raw result](installed-smoke.json). The underlying revision's five GPU stages, six kernel-contract groups, long generation and teacher-forced comparisons are recorded in [its test receipt](../2026-10-30-tensorfold-b3ab9ba/README.md). It is an opt-in engine; the installed MLX service remains the default. Upstream PR 1544 is open, and the announced TensorFold submit-batching improvement is not included in this revision. + +Rebuild with `MSERIES_TENSORFOLD_ROOT=... bash mseries/local-ai/tensorfold-vulkan.sh build`, then `MSERIES_TENSORFOLD_ROOT=... bash mseries/local-ai/package-tensorfold-vulkan.sh OUTPUT.deb`. Install the resulting package with `sudo dpkg -i OUTPUT.deb`. Run `mseries-tensorfold serve MODEL_DIR --backend vulkan --no-update-check` for a compatible Qwen3.5-2B checkpoint; use loopback binding and existing resource policy. diff --git a/mseries/local-ai/receipts/2026-10-30-tensorfold-package/installed-smoke.json b/mseries/local-ai/receipts/2026-10-30-tensorfold-package/installed-smoke.json new file mode 100644 index 00000000..19085030 --- /dev/null +++ b/mseries/local-ai/receipts/2026-10-30-tensorfold-package/installed-smoke.json @@ -0,0 +1,212 @@ +{ + "status": "complete", + "model_key": "qwen2b", + "model": "mlx-community/Qwen3.5-2B-MLX-4bit@93760be4f1f69842a46bc13dbdc0f19e291392a3", + "weights_sha256": "713fe7e5d3c3965f7106b0d0ee17615f7869c23c8d327996df8c1196fbcf07d5", + "kernel": "7.2.11-elementary-aurora72-wip5", + "driver": "Honeykrisp Mesa 9323171c12", + "tensorfold_rev": "b3ab9ba217a80c63fcc48f7643ddfe339a8f4bdc", + "rapid_rev": "1d09cdb684bbf58fac130f5804b08ee2e1f56558", + "serve_concurrency": 4, + "prompt_sha256": "af125b15dfba55caedb541e89e59fe1252c0f13553e5ce6bc181e237fada9926", + "max_tokens": 32, + "policy": { + "reserve_gib": 6, + "max_fan_rpm": null, + "max_temp_c": 80 + }, + "allowance_gib": 4.5, + "arms": [ + "tensorfold" + ], + "concurrency": 1, + "runs": [ + { + "arm": "tensorfold", + "stopped": null, + "samples": [ + { + "t": 0.01, + "available_gib": 19.716, + "max_temp_c": 40.244, + "max_fan_rpm": 0.0 + }, + { + "t": 1.02, + "available_gib": 18.401, + "max_temp_c": 39.671, + "max_fan_rpm": 0.0 + }, + { + "t": 2.03, + "available_gib": 18.4, + "max_temp_c": 40.489, + "max_fan_rpm": 0.0 + }, + { + "t": 3.04, + "available_gib": 18.377, + "max_temp_c": 40.173, + "max_fan_rpm": 0.0 + }, + { + "t": 4.06, + "available_gib": 18.373, + "max_temp_c": 39.99, + "max_fan_rpm": 0.0 + }, + { + "t": 5.08, + "available_gib": 18.381, + "max_temp_c": 40.815, + "max_fan_rpm": 0.0 + }, + { + "t": 6.09, + "available_gib": 18.384, + "max_temp_c": 39.454, + "max_fan_rpm": 0.0 + }, + { + "t": 7.11, + "available_gib": 18.384, + "max_temp_c": 39.563, + "max_fan_rpm": 0.0 + }, + { + "t": 8.12, + "available_gib": 18.385, + "max_temp_c": 40.048, + "max_fan_rpm": 0.0 + }, + { + "t": 9.14, + "available_gib": 18.404, + "max_temp_c": 39.459, + "max_fan_rpm": 0.0 + }, + { + "t": 10.17, + "available_gib": 18.388, + "max_temp_c": 40.423, + "max_fan_rpm": 0.0 + }, + { + "t": 11.19, + "available_gib": 18.399, + "max_temp_c": 41.0, + "max_fan_rpm": 0.0 + }, + { + "t": 12.21, + "available_gib": 18.384, + "max_temp_c": 40.358, + "max_fan_rpm": 0.0 + }, + { + "t": 13.23, + "available_gib": 18.381, + "max_temp_c": 40.929, + "max_fan_rpm": 0.0 + }, + { + "t": 14.25, + "available_gib": 18.308, + "max_temp_c": 40.304, + "max_fan_rpm": 0.0 + }, + { + "t": 15.26, + "available_gib": 18.317, + "max_temp_c": 40.119, + "max_fan_rpm": 0.0 + }, + { + "t": 16.3, + "available_gib": 18.317, + "max_temp_c": 40.913, + "max_fan_rpm": 0.0 + }, + { + "t": 17.3, + "available_gib": 18.317, + "max_temp_c": 41.0, + "max_fan_rpm": 0.0 + }, + { + "t": 18.32, + "available_gib": 18.319, + "max_temp_c": 39.99, + "max_fan_rpm": 0.0 + }, + { + "t": 19.34, + "available_gib": 18.332, + "max_temp_c": 40.929, + "max_fan_rpm": 0.0 + }, + { + "t": 20.35, + "available_gib": 18.347, + "max_temp_c": 40.489, + "max_fan_rpm": 0.0 + }, + { + "t": 21.36, + "available_gib": 18.343, + "max_temp_c": 39.936, + "max_fan_rpm": 0.0 + }, + { + "t": 22.37, + "available_gib": 18.361, + "max_temp_c": 41.108, + "max_fan_rpm": 0.0 + }, + { + "t": 23.38, + "available_gib": 18.342, + "max_temp_c": 40.913, + "max_fan_rpm": 0.0 + }, + { + "t": 24.39, + "available_gib": 18.359, + "max_temp_c": 39.828, + "max_fan_rpm": 0.0 + }, + { + "t": 25.41, + "available_gib": 18.376, + "max_temp_c": 41.195, + "max_fan_rpm": 0.0 + } + ], + "log_tail": "[tensorfold] prompt chunks of up to 32 tokens, cut at replies 256+ tokens apart\nwarning: libtfgrammar.so is required for structured output and required tool calls (HTTP 400 when missing); restore the matching release archive's lib/ directory or run zig build grammar -Dxgrammar=DIR; prepare DIR with git clone --branch v0.2.8 --recurse-submodules https://github.com/mlc-ai/xgrammar\n[tensorfold] serving qwen35-2b at http://127.0.0.1:53303/v1 (sampling: greedy; drafts: none; context: 1024; loaded in 0.5s)\n[tensorfold] 127.0.0.1 \"GET /health HTTP/1.1\" 200 -\n[tensorfold] 127.0.0.1 \"GET /v1/models HTTP/1.1\" 200 -\n[tensorfold] 127.0.0.1 \"POST /v1/chat/completions HTTP/1.1\" 200 -\n[tensorfold] done chatcmpl-c924d09bd37ff2f506062a87c485f290 prompt=27 cached=0 thinking=False effort=none tokens=32 sha=778f678f6c4f finish=length rounds=31 accepted=0/0 ttft=1.953 decode=10.733 elapsed=12.686 queued=0.000 tok/s=2.9\n[tensorfold] 127.0.0.1 \"POST /v1/chat/completions HTTP/1.1\" 200 -\n[tensorfold] done chatcmpl-9cbbdce6a2aff72b8fb44c63b3aa0e7c prompt=27 cached=0 thinking=False effort=none tokens=32 sha=778f678f6c4f finish=length rounds=31 accepted=0/0 ttft=1.975 decode=10.661 elapsed=12.636 queued=0.000 tok/s=2.9\n[tensorfold] shutting down (SIGTERM): draining: new requests get 503; 0 request(s) in progress may finish within 20.0 s\n[tensorfold] shutting down: drained\n[tensorfold] shutting down: stopped\n", + "ready_s": 13.196554299000127, + "ttft_s": 1.9769542879994333, + "total_s": 12.637987194999369, + "completion_tokens": 32, + "token_count_source": "usage", + "text": "Sunlight travels through the atmosphere and gets refracted (bent) as it enters a raindrop.\nThe light then reflects off the back of the dro", + "served_model": "qwen35-2b", + "text_sha256": "ffbf23cb27bb50106bf151750276ab30f61683b0bec1ceb077bab1d61844393e", + "decode_tps": 2.907785790591237, + "min_available_gib": 18.308, + "peak_temp_c": 41.195, + "round": 1, + "admission_available_gib": 19.716 + } + ], + "summary": { + "tensorfold": { + "ok_runs": 1, + "median_ttft_s": 1.9769542879994333, + "median_total_s": 12.637987194999369, + "median_decode_tps": 2.907785790591237, + "texts": [ + "ffbf23cb27bb" + ] + } + } +} diff --git a/mseries/local-ai/tensorfold-launcher.sh b/mseries/local-ai/tensorfold-launcher.sh new file mode 100755 index 00000000..a09213d5 --- /dev/null +++ b/mseries/local-ai/tensorfold-launcher.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# Select the private Apple GPU compute ICD for this process only. +set -euo pipefail +icd=${MSERIES_HK_ICD:-$HOME/.local/share/mlx-omarchy/vulkan/honeykrisp_icd.aarch64.json} +[[ -r $icd ]] || { echo "Private Honeykrisp Vulkan ICD not found: $icd" >&2; exit 1; } +export VK_DRIVER_FILES=$icd +exec /usr/libexec/mseries/tensorfold-native-vulkan "$@" diff --git a/mseries/local-ai/tensorfold-vulkan.sh b/mseries/local-ai/tensorfold-vulkan.sh index 65856cea..1b9ee03a 100755 --- a/mseries/local-ai/tensorfold-vulkan.sh +++ b/mseries/local-ai/tensorfold-vulkan.sh @@ -18,8 +18,7 @@ set -euo pipefail TF_REPO=https://github.com/ashhart/TensorFold.git # PR 1544 contains PR 1526's commits (runtime, Q4 projection, QMM contract), so one tree # builds the server, the stage gates and every contract runner. -TF_REF=pull/1544/head -TF_REV=92bb49db5c313c105cbf4b44638d1e1896615174 +TF_REV=b3ab9ba217a80c63fcc48f7643ddfe339a8f4bdc ZIG_VERSION=0.17.0 ZIG_URL=https://ziglang.org/download/0.17.0/zig-aarch64-linux-0.17.0.tar.xz ZIG_SHA256=9e8d11661d4ae3bd57702a3832781e23ad151dde5798e16a5ccd503f65234ff8 @@ -52,13 +51,13 @@ fetch() { fi [[ -d $SRC/.git ]] || git init -q "$SRC" if [[ $(git -C "$SRC" rev-parse -q --verify HEAD 2>/dev/null) != "$TF_REV" ]]; then - git -C "$SRC" fetch -q --depth 1 "$TF_REPO" "$TF_REF" + git -C "$SRC" fetch -q --depth 1 "$TF_REPO" "$TF_REV" git -C "$SRC" checkout -q --detach FETCH_HEAD fi local have have=$(git -C "$SRC" rev-parse HEAD) if [[ $have != "$TF_REV" ]]; then - echo "PR head moved: have $have, pinned $TF_REV; review the new commits, then bump TF_REV" >&2 + echo "unexpected TensorFold checkout: have $have, pinned $TF_REV" >&2 exit 1 fi } diff --git a/mseries/local-ai/upstreams.json b/mseries/local-ai/upstreams.json index 3a86e38f..8dc39204 100644 --- a/mseries/local-ai/upstreams.json +++ b/mseries/local-ai/upstreams.json @@ -117,9 +117,9 @@ "tensorfold": { "repo": "ashhart/TensorFold", "track": null, - "pin": "92bb49db5c313c105cbf4b44638d1e1896615174", + "pin": "b3ab9ba217a80c63fcc48f7643ddfe339a8f4bdc", "pin_label": "PR 1544 (stacked on PR 1526; supersedes PR 1517)", - "step_rule": "Pin a PR head by hand after reading its new commits; tensorfold-vulkan.sh refuses a moved head. Built and run by the owner (fetched code).", + "step_rule": "Pin an exact public source revision; tensorfold-vulkan.sh fetches and verifies that commit.", "patches": [] } } diff --git a/mseries/nvidia-egpu/README.md b/mseries/nvidia-egpu/README.md new file mode 100644 index 00000000..7aa4e332 --- /dev/null +++ b/mseries/nvidia-egpu/README.md @@ -0,0 +1,13 @@ +# NVIDIA RTX eGPU source kit + +This prepares NVIDIA's open GPU kernel modules **595.112.07**, source commit `d8257d95ae5fed364163e4df51db66df8b1ee0d2`, with Joshua Warren's public [16K page-size fix #1480](https://github.com/NVIDIA/open-gpu-kernel-modules/pull/1480), [ARM64 DMA submap fix #1481](https://github.com/NVIDIA/open-gpu-kernel-modules/pull/1481), and [Apple tunnel chipset fix #1482](https://github.com/NVIDIA/open-gpu-kernel-modules/pull/1482). Their original patches and SHA-256 pins are retained with authorship; the NVIDIA source includes its own licenses. + +```sh +bash mseries/nvidia-egpu/prepare.sh "$HOME/.cache/mseries-nvidia-595-prepared" +``` + +The preparation checks the exact source commit and patch hashes and applies all three changes; it does not build, install, or load modules. Building the NVIDIA module requires a fully prepared, version-matched kernel build tree and matching NVIDIA 595.112.07 userspace. Neither is provided by the current kernel payload package. Do not load a module built against a different kernel. No RTX card is connected here. + +`package-source.sh PREPARED_SOURCE OUTPUT.deb` packages the prepared source and licenses without building or loading a driver. It verifies the source revision, patch hashes, complete tracked diff and absence of untracked files. The resulting `mseries-nvidia-open-source` `595.112.07+apple.1-1` package was installed locally on 2026-10-30; its SHA-256 is `a397b68ad14cb04aab3f3eefca20bdcd6a7d07b21e98eea818e801f6b209ab23`, and the source is in `/usr/src/mseries-nvidia-open-595.112.07`. This package contains source, not an NVIDIA kernel module or CUDA userspace. + +The original #1482 patch targets NVIDIA 615 and is retained unchanged as a reference. Its 595 rebase keeps all 29 added lines byte-identical, retains the older tree's reserved chipset value, and omits unrelated TB500 context. The 595 source kit applies that rebase after the two 16K fixes. The upstream 595 setup also describes `RmForceDisableIomapWC=1` as an alternative; no automatic modprobe configuration is installed here. The current downstream combined kernel separately enables Nouveau/NVK; it is not the NVIDIA-open/CUDA path. Firmware `linux-firmware-nvidia-graphics` `20260319.git217ca6e4-0ubuntu1.1` is installed, including GB206 570.144 firmware; this does not install the NVIDIA-open module or CUDA userspace. See the upstream [RTX setup](https://github.com/joshuaswarren/omarchy-egpu/blob/main/cards/rtx-5060-ti/setup.md). diff --git a/mseries/nvidia-egpu/package-source.sh b/mseries/nvidia-egpu/package-source.sh new file mode 100755 index 00000000..82c5fa18 --- /dev/null +++ b/mseries/nvidia-egpu/package-source.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# Package the prepared NVIDIA source kit only; no module is built or loaded. +set -euo pipefail +here=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) +src=${1:?usage: package-source.sh PREPARED_SOURCE NEW_OUTPUT_DEB} +out=${2:?usage: package-source.sh PREPARED_SOURCE NEW_OUTPUT_DEB} +[[ ! -e $out ]] || { echo 'Refusing to overwrite output' >&2; exit 1; } +[[ $(git -C "$src" rev-parse HEAD) == d8257d95ae5fed364163e4df51db66df8b1ee0d2 ]] || { echo 'Unexpected NVIDIA source revision' >&2; exit 1; } +(cd "$here/patches" && sha256sum -c SHA256SUMS) +[[ -z $(git -C "$src" ls-files --others --exclude-standard) ]] || { echo "Untracked files in source" >&2; exit 1; } +[[ $(git -C "$src" diff --binary | sha256sum | cut -d" " -f1) == 6bdbb8493e7c14c5ca9e62ff7547aca075923759718bd233a2bbecbf1f489943 ]] || { echo "Unexpected source changes" >&2; exit 1; } +for patch in 0001-16k-system-page.patch 0002-arm64-dma-submap.patch 0003-apple-tunnel-595.patch; do + git -C "$src" apply --reverse --check "$here/patches/$patch" +done +work=$(mktemp -d "${TMPDIR:-/tmp}/mseries-nvidia-source.XXXXXXXX") +trap 'rm -rf "$work"' EXIT +mkdir -p "$work/DEBIAN" "$work/usr/src/mseries-nvidia-open-595.112.07" "$work/usr/share/doc/mseries-nvidia-open-source" +tar -C "$src" --exclude=.git -cf - . | tar -C "$work/usr/src/mseries-nvidia-open-595.112.07" -xf - +cp "$src/COPYING" "$work/usr/share/doc/mseries-nvidia-open-source/copyright" +cp "$here/README.md" "$work/usr/share/doc/mseries-nvidia-open-source/README" +cat > "$work/DEBIAN/control" <<'CONTROL' +Package: mseries-nvidia-open-source +Version: 595.112.07+apple.1-1 +Architecture: all +Maintainer: ElementaryOS MSeries contributors +Section: kernel +Priority: optional +Description: NVIDIA open GPU module source with Apple Silicon fixes + Prepared source only; requires matching kernel build tree and NVIDIA userspace. +CONTROL +dpkg-deb --root-owner-group --build "$work" "$out" diff --git a/mseries/nvidia-egpu/patches/0001-16k-system-page.patch b/mseries/nvidia-egpu/patches/0001-16k-system-page.patch new file mode 100644 index 00000000..e9119713 --- /dev/null +++ b/mseries/nvidia-egpu/patches/0001-16k-system-page.patch @@ -0,0 +1,69 @@ +From dae4d562c718e98b0fcc4ea86bd25e4dbb807b61 Mon Sep 17 00:00:00 2001 +From: Joshua Warren <816217+joshuaswarren@users.noreply.github.com> +Date: Fri, 23 Oct 2026 15:45:11 -0500 +Subject: [PATCH] mem_mgr: handle 16K system memory page size + +On kernels configured with a 16K page size, the default system memory +page size picked by _memmgrPickDefaultSysmemPageSize is the OS page size +(16384). The page size switch at the end of memmgrDeterminePageSize only +knows 4K, 64K, 128K, 2M, 512M and 256G, so a 16K value fell through to +the default case, made the function return 0, and every RM init path +that allocates system memory through it (channel, scrubber and ce_utils +buffers among them) failed with NV_ERR_INVALID_STATE: +"memmgrDeterminePageSize failed, status 0x40". + +Recognise the 16K OS page size: keep returning it so callers size and +align to the OS page, but report the 4K PTE page size attribute, since +the GPU walks the allocation with small pages. 4K, 64K-page and larger +kernels take exactly the same path as before. + +Observed on a GeForce RTX 5060 Ti (GB206) in an arm64 kernel with 16K +pages, 595.112.07 branch with the companion nv-linux submap patch applied: + + NVRM: GPU0 memmgrAllocResources_IMPL: memmgrDeterminePageSize failed, status: 0x40 + NVRM: GPU0 RmInitNvDevice: *** Cannot load state into the device + NVRM: GPU 0001:03:00.0: RmInitAdapter failed! (0x25:0x40:1373) + +Tested: the same card on a 16-inch M1 Max MacBook Pro over a USB4 PCIe +tunnel with this change: nvidia-smi lists the card, a CUDA context and a +1 GiB allocation succeed, and llama.cpp inference runs on it. A stock +module without either patch was not run. + +Signed-off-by: Joshua Warren <816217+joshuaswarren@users.noreply.github.com> +--- + src/nvidia/inc/kernel/gpu/mem_mgr/rm_page_size.h | 1 + + src/nvidia/src/kernel/gpu/mem_mgr/mem_mgr.c | 9 +++++++++ + 2 files changed, 10 insertions(+) + +diff --git a/src/nvidia/inc/kernel/gpu/mem_mgr/rm_page_size.h b/src/nvidia/inc/kernel/gpu/mem_mgr/rm_page_size.h +index f25795eac9..9639255d8c 100644 +--- a/src/nvidia/inc/kernel/gpu/mem_mgr/rm_page_size.h ++++ b/src/nvidia/inc/kernel/gpu/mem_mgr/rm_page_size.h +@@ -36,6 +36,7 @@ + //--------------------------------------------------------------------------- + #define RM_PAGE_SIZE_INVALID 0 + #define RM_PAGE_SIZE 4096 ++#define RM_PAGE_SIZE_16K (16 * 1024) + #define RM_PAGE_SIZE_64K (64 * 1024) + #define RM_PAGE_SIZE_128K (128 * 1024) + #define RM_PAGE_MASK 0x0FFF +diff --git a/src/nvidia/src/kernel/gpu/mem_mgr/mem_mgr.c b/src/nvidia/src/kernel/gpu/mem_mgr/mem_mgr.c +index 73bebf9740..c3ce8384f8 100644 +--- a/src/nvidia/src/kernel/gpu/mem_mgr/mem_mgr.c ++++ b/src/nvidia/src/kernel/gpu/mem_mgr/mem_mgr.c +@@ -1957,6 +1957,15 @@ memmgrDeterminePageSize_IMPL + *pRetAttr = FLD_SET_DRF(OS32, _ATTR, _PAGE_SIZE, _BIG, *pRetAttr); + break; + ++ // ++ // On kernels that use a 16K page size, system memory is carved at the ++ // OS page size but is walked with 4K PTEs, so report the small-page ++ // attribute while returning the OS page size for alignment and sizing. ++ // ++ case RM_PAGE_SIZE_16K: ++ *pRetAttr = FLD_SET_DRF(OS32, _ATTR, _PAGE_SIZE, _4KB, *pRetAttr); ++ break; ++ + case RM_PAGE_SIZE_HUGE: + *pRetAttr = FLD_SET_DRF(OS32, _ATTR, _PAGE_SIZE, _HUGE, *pRetAttr); + *pRetAttr2 = FLD_SET_DRF(OS32, _ATTR2, _PAGE_SIZE_HUGE, _2MB, *pRetAttr2); diff --git a/mseries/nvidia-egpu/patches/0002-arm64-dma-submap.patch b/mseries/nvidia-egpu/patches/0002-arm64-dma-submap.patch new file mode 100644 index 00000000..3aea585d --- /dev/null +++ b/mseries/nvidia-egpu/patches/0002-arm64-dma-submap.patch @@ -0,0 +1,53 @@ +From 2046c946ff81ee0b9fc0ce5864329af4f2252c2d Mon Sep 17 00:00:00 2001 +From: Joshua Warren <816217+joshuaswarren@users.noreply.github.com> +Date: Fri, 23 Oct 2026 10:29:46 -0500 +Subject: [PATCH] nv-linux: keep DMA submaps 2M aligned on every arm64 page + size + +The IOVA range covered by one DMA submap has to start on a 2M boundary, +otherwise a 2M system memory allocation gets split across two submaps. +The rule was only applied when CONFIG_ARM64_4K_PAGES was set. On arm64 +kernels with 16K or 64K pages the generic branch was taken, which rounds +the submap size down to 4G minus one page and therefore leaves the +mapped IOVA range unaligned. + +Round the page count down to a whole number of 2M blocks on arm64 +kernels with 16K or 64K pages. With 16K pages this makes a submap 4G-2M +instead of 4G-16K, so submap starts stay 2M aligned. 4K page arm64 +kernels keep the value they have today, and other architectures are +unchanged. + +Tested together with the companion mem_mgr page-size patch: GeForce RTX 5060 Ti +(GB206) on an arm64 kernel with 16K pages (a 16-inch M1 Max MacBook Pro over a +USB4 PCIe tunnel), 595.112.07 branch; nvidia-smi lists the card and CUDA +context creation and a 1 GiB allocation succeed. This change alone has not been +shown necessary on 16K hardware; its effect is derived from the source and from +the 64K-page report in issue #1269. + +Signed-off-by: Joshua Warren <816217+joshuaswarren@users.noreply.github.com> +--- + kernel-open/common/inc/nv-linux.h | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/kernel-open/common/inc/nv-linux.h b/kernel-open/common/inc/nv-linux.h +index f377db1e9..29edde8f8 100644 +--- a/kernel-open/common/inc/nv-linux.h ++++ b/kernel-open/common/inc/nv-linux.h +@@ -1102,6 +1102,17 @@ typedef struct nv_dma_map_s { + #if defined(CONFIG_ARM64_4K_PAGES) + #define NV_DMA_U32_MAX_4K_PAGES ((NvU32)((NV_U32_MAX >> PAGE_SHIFT) + 1)) + #define NV_DMA_SUBMAP_MAX_PAGES ((NvU32)(NV_DMA_U32_MAX_4K_PAGES - 512)) ++#elif defined(CONFIG_ARM64) ++/* ++ * The same 2M alignment rule applies to arm64 kernels that use 16K or 64K pages: ++ * the mapped IOVA range must still start on a 2M boundary, otherwise 2M sysmem ++ * allocations get split across submaps. Round the page count down to a whole ++ * number of 2M blocks instead of assuming that a page is 4K. ++ */ ++#define NV_DMA_SUBMAP_PAGES_PER_2M ((NvU32)((2 * 1024 * 1024) >> PAGE_SHIFT)) ++#define NV_DMA_SUBMAP_MAX_PAGES ((NvU32)((NV_U32_MAX >> PAGE_SHIFT) / \ ++ NV_DMA_SUBMAP_PAGES_PER_2M * \ ++ NV_DMA_SUBMAP_PAGES_PER_2M)) + #else + #define NV_DMA_SUBMAP_MAX_PAGES ((NvU32)(NV_U32_MAX >> PAGE_SHIFT)) + #endif diff --git a/mseries/nvidia-egpu/patches/0003-apple-tunnel-595.patch b/mseries/nvidia-egpu/patches/0003-apple-tunnel-595.patch new file mode 100644 index 00000000..e6a63219 --- /dev/null +++ b/mseries/nvidia-egpu/patches/0003-apple-tunnel-595.patch @@ -0,0 +1,82 @@ +From: Joshua Warren <816217+joshuaswarren@users.noreply.github.com> +Subject: [PATCH] chipset: recognize the Apple Thunderbolt/USB4 tunnel root port + +Rebased from NVIDIA/open-gpu-kernel-modules PR #1482 onto 595.112.07. +Retains 595 reserved chipset value and omits unrelated 615 TB500 context. + +diff --git a/src/common/shared/inc/nvdevid.h b/src/common/shared/inc/nvdevid.h +index f67207d..f2bd1da 100644 +--- a/src/common/shared/inc/nvdevid.h ++++ b/src/common/shared/inc/nvdevid.h +@@ -664,6 +664,7 @@ enum { + , CS_AMPERE_AMPEREONE192 + , CS_NVIDIA_T254 + , CS_RESERVED_5 ++, CS_APPLE_PCIE_TUNNEL_ROOT_PORT + , CS_MAX_PCIE + }; + +diff --git a/src/nvidia/arch/nvalloc/common/inc/nvcst.h b/src/nvidia/arch/nvalloc/common/inc/nvcst.h +index 33d248f..b62b9d6 100644 +--- a/src/nvidia/arch/nvalloc/common/inc/nvcst.h ++++ b/src/nvidia/arch/nvalloc/common/inc/nvcst.h +@@ -99,6 +99,7 @@ CHIPSET_SETUP_FUNC(PLDA_XpressRichAXI_setupFunc) + CHIPSET_SETUP_FUNC(Riscv_generic_setupFunc) + CHIPSET_SETUP_FUNC(Intel_A70D_setupFunc) + CHIPSET_SETUP_FUNC(AMD_14D8_setupFunc) ++CHIPSET_SETUP_FUNC(Apple_TunnelRootPort_setupFunc) + + + // Keep string length <=32 (including termination) to avoid string copy overflow +@@ -313,6 +314,14 @@ CSINFO chipsetInfo[] = + {PCI_VENDOR_ID_AMPERE, 0xE20E, CS_AMPERE_AMPEREONE192, "Ampere AmpereOne-192", Ampere_AmpereOne_setupFunc}, + {PCI_VENDOR_ID_AMPERE, 0xE20F, CS_AMPERE_AMPEREONE192, "Ampere AmpereOne-192", Ampere_AmpereOne_setupFunc}, + {PCI_VENDOR_ID_NVIDIA, 0x22D1, CS_NVIDIA_T254, "T254", Nvidia_T194_setupFunc}, ++ // ++ // Apple Silicon Thunderbolt/USB4 PCIe tunnel root port. Write-combined CPU ++ // mappings of GPU BAR memory do not work through the tunnel on this host ++ // (observed on a 16-inch M1 Max MacBook Pro); map BAR memory as device ++ // memory instead. ++ // ++ {PCI_VENDOR_ID_APPLE, 0x100F, CS_APPLE_PCIE_TUNNEL_ROOT_PORT, ++ "Apple PCIe tunnel root port", Apple_TunnelRootPort_setupFunc}, + + /////////////////////////////////////////////////////////////////////////////////////////////////// + +@@ -441,6 +450,7 @@ ARMCSALLOWLISTINFO armChipsetAllowListInfo[] = + {PCI_VENDOR_ID_AMPERE, 0xE20E, CS_AMPERE_AMPEREONE192}, // Ampere AmpereOne-192 + {PCI_VENDOR_ID_AMPERE, 0xE20F, CS_AMPERE_AMPEREONE192}, // Ampere AmpereOne-192 + {PCI_VENDOR_ID_NVIDIA, 0x22D1, CS_NVIDIA_T254}, ++ {PCI_VENDOR_ID_APPLE, 0x100F, CS_APPLE_PCIE_TUNNEL_ROOT_PORT}, // Apple PCIe tunnel root port + + // last element must have chipset CS_UNKNOWN (zero) + {0, 0, CS_UNKNOWN} +diff --git a/src/nvidia/src/kernel/platform/chipset/chipset_info.c b/src/nvidia/src/kernel/platform/chipset/chipset_info.c +index 14791bb..e1f8390 100644 +--- a/src/nvidia/src/kernel/platform/chipset/chipset_info.c ++++ b/src/nvidia/src/kernel/platform/chipset/chipset_info.c +@@ -1278,6 +1278,24 @@ Mellanox_BlueField3_setupFunc + return NV_OK; + } + ++// Apple PCIe tunnel root port Setup Function ++static NV_STATUS ++Apple_TunnelRootPort_setupFunc ++( ++ OBJCL *pCl ++) ++{ ++ // Apple Silicon Thunderbolt/USB4 PCIe tunnel root port (PCI vendor 0x106b, ++ // device 0x100f). Write-combined CPU mappings of GPU BAR memory do not ++ // work through the tunnel on this host; map BAR memory as device memory. ++ // Observed on a 16-inch M1 Max MacBook Pro: with the write-combining ++ // default the first adapter initialisation reset the machine, and with ++ // this property forced through the RmForceDisableIomapWC registry key the ++ // adapter initialises and runs CUDA without errors. ++ pCl->setProperty(pCl, PDB_PROP_CL_DISABLE_IOMAP_WC, NV_TRUE); ++ return NV_OK; ++} ++ + + // Amazon Gravitron2 Setup Function + static NV_STATUS diff --git a/mseries/nvidia-egpu/patches/SHA256SUMS b/mseries/nvidia-egpu/patches/SHA256SUMS new file mode 100644 index 00000000..1f5d6a8c --- /dev/null +++ b/mseries/nvidia-egpu/patches/SHA256SUMS @@ -0,0 +1,4 @@ +9ee1f0fab986a86910e91bc2834b1df4deab420207b5dd37683c21c49c486302 0001-16k-system-page.patch +80b408bf313083528d70ccf09869ac23207c89f20a9e8edbcbaeebe58dcfeaf0 0002-arm64-dma-submap.patch +dcbe644974ca95baadfb919a13d6eb91f9c54bf3728c6e9ee2f1bd2f9397a98a upstream-1482-615-reference.patch +222f5de32ab31c6482de2583690ed891bf22725a5589891b0fb916d5050adb10 0003-apple-tunnel-595.patch diff --git a/mseries/nvidia-egpu/patches/upstream-1482-615-reference.patch b/mseries/nvidia-egpu/patches/upstream-1482-615-reference.patch new file mode 100644 index 00000000..fad8e765 --- /dev/null +++ b/mseries/nvidia-egpu/patches/upstream-1482-615-reference.patch @@ -0,0 +1,104 @@ +From 06133ab30250b85aff56a6bddb34c171d88726bb Mon Sep 17 00:00:00 2001 +From: Joshua Warren <816217+joshuaswarren@users.noreply.github.com> +Date: Sat, 24 Oct 2026 03:19:30 +0000 +Subject: [PATCH] chipset: recognize the Apple Thunderbolt/USB4 tunnel root + port + +On an arm64 host whose GPU sits behind the Apple Thunderbolt/USB4 PCIe +tunnel root port (PCI vendor 0x106b, device 0x100f), write-combined CPU +mappings of the GPU BAR memory do not work: with the default +write-combining mapping the first adapter initialisation reset the +machine. Forcing PDB_PROP_CL_DISABLE_IOMAP_WC (the mechanism this +driver already uses for BlueField 3 and Ampere Altra rev 0) maps the +BAR as device memory, and the adapter initialises and runs CUDA +without errors. + +Add a chipset entry with a setup function that sets +PDB_PROP_CL_DISABLE_IOMAP_WC, and allow-list the root port for ARM so +the "Chipset not recognized" disclaimer is not printed for a host we +now recognise. Observed on a 16-inch M1 Max MacBook Pro with a GeForce +RTX 5060 Ti; only this one root-port device ID is added. + +Signed-off-by: Joshua Warren <816217+joshuaswarren@users.noreply.github.com> +--- + src/common/shared/inc/nvdevid.h | 1 + + src/nvidia/arch/nvalloc/common/inc/nvcst.h | 10 ++++++++++ + .../src/kernel/platform/chipset/chipset_info.c | 18 ++++++++++++++++++ + 3 files changed, 29 insertions(+) + +diff --git a/src/common/shared/inc/nvdevid.h b/src/common/shared/inc/nvdevid.h +index f43a8448d0..b9ee59828b 100644 +--- a/src/common/shared/inc/nvdevid.h ++++ b/src/common/shared/inc/nvdevid.h +@@ -667,6 +667,7 @@ enum { + , CS_AMPERE_AMPEREONE192 + , CS_NVIDIA_T254 + , CS_NVIDIA_TB500 ++, CS_APPLE_PCIE_TUNNEL_ROOT_PORT + , CS_MAX_PCIE + }; + +diff --git a/src/nvidia/arch/nvalloc/common/inc/nvcst.h b/src/nvidia/arch/nvalloc/common/inc/nvcst.h +index f2ec1cf0de..4de775c92b 100644 +--- a/src/nvidia/arch/nvalloc/common/inc/nvcst.h ++++ b/src/nvidia/arch/nvalloc/common/inc/nvcst.h +@@ -100,6 +100,7 @@ CHIPSET_SETUP_FUNC(Riscv_generic_setupFunc) + CHIPSET_SETUP_FUNC(Intel_A70D_setupFunc) + CHIPSET_SETUP_FUNC(AMD_14D8_setupFunc) + CHIPSET_SETUP_FUNC(Nvidia_TB500_setupFunc) ++CHIPSET_SETUP_FUNC(Apple_TunnelRootPort_setupFunc) + + + // Keep string length <=32 (including termination) to avoid string copy overflow +@@ -320,6 +321,14 @@ CSINFO chipsetInfo[] = + {PCI_VENDOR_ID_AMPERE, 0xE20E, CS_AMPERE_AMPEREONE192, "Ampere AmpereOne-192", Ampere_AmpereOne_setupFunc}, + {PCI_VENDOR_ID_AMPERE, 0xE20F, CS_AMPERE_AMPEREONE192, "Ampere AmpereOne-192", Ampere_AmpereOne_setupFunc}, + {PCI_VENDOR_ID_NVIDIA, 0x22D1, CS_NVIDIA_T254, "T254", Nvidia_T194_setupFunc}, ++ // ++ // Apple Silicon Thunderbolt/USB4 PCIe tunnel root port. Write-combined CPU ++ // mappings of GPU BAR memory do not work through the tunnel on this host ++ // (observed on a 16-inch M1 Max MacBook Pro); map BAR memory as device ++ // memory instead. ++ // ++ {PCI_VENDOR_ID_APPLE, 0x100F, CS_APPLE_PCIE_TUNNEL_ROOT_PORT, ++ "Apple PCIe tunnel root port", Apple_TunnelRootPort_setupFunc}, + + /////////////////////////////////////////////////////////////////////////////////////////////////// + +@@ -454,6 +463,7 @@ ARMCSALLOWLISTINFO armChipsetAllowListInfo[] = + {PCI_VENDOR_ID_AMPERE, 0xE20F, CS_AMPERE_AMPEREONE192}, // Ampere AmpereOne-192 + {PCI_VENDOR_ID_NVIDIA, 0x3400, CS_NVIDIA_T268}, // NVIDIA Tegra RP iGPU + {PCI_VENDOR_ID_NVIDIA, 0x22D1, CS_NVIDIA_T254}, ++ {PCI_VENDOR_ID_APPLE, 0x100F, CS_APPLE_PCIE_TUNNEL_ROOT_PORT}, // Apple PCIe tunnel root port + + // last element must have chipset CS_UNKNOWN (zero) + {0, 0, CS_UNKNOWN} +diff --git a/src/nvidia/src/kernel/platform/chipset/chipset_info.c b/src/nvidia/src/kernel/platform/chipset/chipset_info.c +index 34d8776c04..56fa83664d 100644 +--- a/src/nvidia/src/kernel/platform/chipset/chipset_info.c ++++ b/src/nvidia/src/kernel/platform/chipset/chipset_info.c +@@ -1295,6 +1295,24 @@ Mellanox_BlueField3_setupFunc + return NV_OK; + } + ++// Apple PCIe tunnel root port Setup Function ++static NV_STATUS ++Apple_TunnelRootPort_setupFunc ++( ++ OBJCL *pCl ++) ++{ ++ // Apple Silicon Thunderbolt/USB4 PCIe tunnel root port (PCI vendor 0x106b, ++ // device 0x100f). Write-combined CPU mappings of GPU BAR memory do not ++ // work through the tunnel on this host; map BAR memory as device memory. ++ // Observed on a 16-inch M1 Max MacBook Pro: with the write-combining ++ // default the first adapter initialisation reset the machine, and with ++ // this property forced through the RmForceDisableIomapWC registry key the ++ // adapter initialises and runs CUDA without errors. ++ pCl->setProperty(pCl, PDB_PROP_CL_DISABLE_IOMAP_WC, NV_TRUE); ++ return NV_OK; ++} ++ + + // Amazon Gravitron2 Setup Function + static NV_STATUS diff --git a/mseries/nvidia-egpu/prepare.sh b/mseries/nvidia-egpu/prepare.sh new file mode 100755 index 00000000..97a6b2d3 --- /dev/null +++ b/mseries/nvidia-egpu/prepare.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# Prepare NVIDIA's 595.112.07 open modules plus the public 16K fixes; never install. +set -euo pipefail +here=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) +out=${1:?usage: prepare.sh NEW_OUTPUT_DIRECTORY} +[[ ! -e $out ]] || { echo 'Output already exists' >&2; exit 1; } +mkdir -p "$out" +git -C "$out" init -q +git -C "$out" fetch -q --depth 1 https://github.com/NVIDIA/open-gpu-kernel-modules.git d8257d95ae5fed364163e4df51db66df8b1ee0d2 +git -C "$out" checkout -q --detach FETCH_HEAD +[[ $(git -C "$out" rev-parse HEAD) == d8257d95ae5fed364163e4df51db66df8b1ee0d2 ]] +(cd "$here/patches" && sha256sum -c SHA256SUMS) +for patch in 0001-16k-system-page.patch 0002-arm64-dma-submap.patch 0003-apple-tunnel-595.patch; do + git -C "$out" apply --check "$here/patches/$patch" + git -C "$out" apply "$here/patches/$patch" +done +printf '%s\n' 'Prepared NVIDIA open modules 595.112.07 with PRs 1480, 1481 and rebased 1482; no build or installation.'