fix(amd): coerce gfx1103 (780M) to HSA_OVERRIDE 11.0.0 so it stays on GPU (#1134)
PR #1076 stopped forcing HSA_OVERRIDE_GFX_VERSION=11.0.0 on "natively-supported" AMD iGPUs. That was correct for gfx1150/gfx1151 (Strix 890M / Strix Halo, which are in the bundled rocblas allowlist) but wrong for gfx1103 (Phoenix/Hawk Point 780M/760M), which is NOT in that list. Without the override, ollama drops the 780M with "no rocblas support for gfx target" and falls back to CPU on a fresh AI provision. Extract the gfx→HSA mapping into a pure, unit-tested util and map gfx1103 → 11.0.0 (gfx1100 kernels), the value that worked on v1.33.0 and that restores full GPU offload in the field. gfx1150/1151 stay native. Also harden the installer's 780M detection (Hawk Point / "Radeon 780M/ 760M" strings) so the gfx marker isn't silently deleted, and upgrade the no-marker fallback log from info to warn since it can mask CPU fallback.
This commit is contained in:
parent
f5f2944516
commit
90489e06da
|
|
@ -4,6 +4,7 @@ import logger from '@adonisjs/core/services/logger'
|
|||
import { inject } from '@adonisjs/core'
|
||||
import transmit from '@adonisjs/transmit/services/main'
|
||||
import { doResumableDownloadWithRetry } from '../utils/downloads.js'
|
||||
import { mapGfxToHsaOverride } from '../utils/amd_hsa_override.js'
|
||||
import { join } from 'path'
|
||||
import os from 'node:os'
|
||||
import env from '#start/env'
|
||||
|
|
@ -1477,10 +1478,9 @@ export class DockerService {
|
|||
* gfx1030 (RX 6800/6700/etc.), gfx1100/1101/1102 (RX 7900/7800/7600) are on AMD's
|
||||
* official ROCm allowlist — forcing an override on these breaks GPU discovery.
|
||||
* gfx1035 / gfx1036 (RDNA 2 iGPUs like 680M) need 10.3.0 to coerce to gfx1030.
|
||||
* gfx1103 / gfx1150 / gfx1151 (RDNA 3/3.5 iGPUs like 780M / 890M / Strix Halo) are
|
||||
* natively supported by the ROCm 7.2 the current ollama:rocm image bundles, so they
|
||||
* need NO override. Forcing 11.0.0 coerces them to gfx1100's kernels — unnecessary on
|
||||
* the 890M and a source of faults on the 780M (gfx1100 WMMA instructions it lacks). See #1056.
|
||||
* gfx1150 / gfx1151 (RDNA 3.5 iGPUs like 890M / Strix Halo) ARE on the bundled rocblas
|
||||
* allowlist, so they need NO override. gfx1103 (Phoenix 780M/760M) is NOT — it must be
|
||||
* coerced to 11.0.0 (gfx1100 kernels) or ollama drops it to CPU. See ../utils/amd_hsa_override.ts.
|
||||
*
|
||||
* Resolution order:
|
||||
* 1. KV `ai.amdHsaOverride` — manual user override; accepts 'none' (disable) or a semver-style value.
|
||||
|
|
@ -1536,27 +1536,18 @@ export class DockerService {
|
|||
// install_nomad.sh. Fall through to the default.
|
||||
}
|
||||
|
||||
logger.info('[DockerService] No AMD gfx marker; applying no HSA override (native ROCm discovery)')
|
||||
logger.warn(
|
||||
'[DockerService] AMD GPU configured but no gfx marker (/app/storage/.nomad-amd-gfx) and no ' +
|
||||
'ai.amdHsaOverride KV; relying on native ROCm discovery. iGPUs not on the bundled rocblas ' +
|
||||
'allowlist (e.g. 780M/gfx1103, 680M/gfx1035) will silently fall back to CPU. Set the ' +
|
||||
'ai.amdHsaOverride KV (e.g. 11.0.0 for a 780M) and force-reinstall the AI service if so.'
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
private _mapGfxToHsaOverride(gfx: string): string | null {
|
||||
// Officially supported by ROCm — no override needed
|
||||
if (gfx === 'gfx1030' || gfx === 'gfx1100' || gfx === 'gfx1101' || gfx === 'gfx1102') {
|
||||
return null
|
||||
}
|
||||
// RDNA 2 variants + iGPUs (gfx1031..gfx1036, e.g. Rembrandt 680M) — not natively
|
||||
// supported, still need coercion to gfx1030.
|
||||
if (/^gfx103[1-6]$/.test(gfx)) {
|
||||
return '10.3.0'
|
||||
}
|
||||
// RDNA 3 / 3.5 mobile parts (Phoenix 780M = gfx1103, Strix 890M = gfx1150, Strix Halo =
|
||||
// gfx1151) are native under ROCm 7.2 — no override (forcing 11.0.0 faults the 780M). See #1056.
|
||||
if (gfx === 'gfx1103' || gfx === 'gfx1150' || gfx === 'gfx1151') {
|
||||
return null
|
||||
}
|
||||
// Unknown/newer target: prefer native discovery over a coercion that's likely wrong.
|
||||
return null
|
||||
// Pure mapping lives in ../utils/amd_hsa_override.ts so it stays unit-testable.
|
||||
return mapGfxToHsaOverride(gfx)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* Map an AMD GPU's gfx target to the `HSA_OVERRIDE_GFX_VERSION` value the ollama:rocm
|
||||
* container needs, or `null` when the card is discovered natively and no override should
|
||||
* be applied.
|
||||
*
|
||||
* This is intentionally a pure function so the mapping is unit-testable without
|
||||
* constructing the Docker service or touching the container runtime. `DockerService`
|
||||
* delegates its private `_mapGfxToHsaOverride` to this.
|
||||
*
|
||||
* The bundled `ollama/ollama:rocm` rocblas ships kernels for a fixed allowlist — as seen
|
||||
* in ollama's own startup log:
|
||||
* supported=[gfx1030, gfx1100/1101/1102, gfx1150/1151, gfx1200/1201, gfx908/90a/942/950]
|
||||
* A target NOT in that list is dropped to CPU unless we coerce it onto a supported one via
|
||||
* HSA_OVERRIDE_GFX_VERSION.
|
||||
*
|
||||
* Mapping:
|
||||
* - gfx1030 / gfx1100 / gfx1101 / gfx1102 → none. Discrete RDNA 2/3 on the allowlist;
|
||||
* forcing an override here breaks GPU discovery.
|
||||
* - gfx1150 / gfx1151 (Strix 890M, Strix Halo) → none. RDNA 3.5 iGPUs that ARE on the
|
||||
* allowlist under the bundled ROCm, so native discovery works. (#1076 got this right.)
|
||||
* - gfx1103 (Phoenix/Hawk Point 780M/760M) → '11.0.0'. RDNA 3 iGPU that is NOT on the
|
||||
* allowlist, so it must be coerced onto gfx1100's kernels. #1076 wrongly grouped it with
|
||||
* gfx1150/1151 and dropped the override, silently sending the 780M to CPU (the very
|
||||
* common iGPU this regression hit). 11.0.0 is the value that worked on v1.33.0 and that
|
||||
* restores full GPU offload in the field; #1076's "gfx1100 WMMA fault" theory did not
|
||||
* hold up.
|
||||
* - gfx1031..gfx1036 (RDNA 2 iGPUs, e.g. Rembrandt 680M) → '10.3.0'. Not on the allowlist;
|
||||
* coerce onto gfx1030.
|
||||
* - anything else (unknown/newer target) → none. Prefer native discovery over a coercion
|
||||
* that's likely wrong; a hardcoded default gets more wrong as ROCm adds native targets.
|
||||
*/
|
||||
export function mapGfxToHsaOverride(gfx: string): string | null {
|
||||
// Officially supported by the bundled ROCm — no override needed.
|
||||
if (gfx === 'gfx1030' || gfx === 'gfx1100' || gfx === 'gfx1101' || gfx === 'gfx1102') {
|
||||
return null
|
||||
}
|
||||
// RDNA 3.5 iGPUs (Strix 890M = gfx1150, Strix Halo = gfx1151) — natively supported.
|
||||
if (gfx === 'gfx1150' || gfx === 'gfx1151') {
|
||||
return null
|
||||
}
|
||||
// RDNA 3 Phoenix/Hawk Point (780M/760M = gfx1103) — NOT on the rocblas allowlist; coerce
|
||||
// to gfx1100 kernels or ollama drops it to CPU.
|
||||
if (gfx === 'gfx1103') {
|
||||
return '11.0.0'
|
||||
}
|
||||
// RDNA 2 variants + iGPUs (gfx1031..gfx1036, e.g. Rembrandt 680M) — coerce to gfx1030.
|
||||
if (/^gfx103[1-6]$/.test(gfx)) {
|
||||
return '10.3.0'
|
||||
}
|
||||
// Unknown/newer target: prefer native discovery over a coercion that's likely wrong.
|
||||
return null
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import * as assert from 'node:assert/strict'
|
||||
import { test } from 'node:test'
|
||||
|
||||
import { mapGfxToHsaOverride } from '../../app/utils/amd_hsa_override.js'
|
||||
|
||||
test('gfx1103 (Phoenix 780M) coerces to 11.0.0 — the #1076 regression', () => {
|
||||
// Not on the bundled rocblas allowlist; without this ollama drops it to CPU.
|
||||
assert.equal(mapGfxToHsaOverride('gfx1103'), '11.0.0')
|
||||
})
|
||||
|
||||
test('gfx1150 / gfx1151 (Strix 890M, Strix Halo) stay native — no override', () => {
|
||||
// These ARE on the allowlist; forcing an override would needlessly coerce them.
|
||||
assert.equal(mapGfxToHsaOverride('gfx1150'), null)
|
||||
assert.equal(mapGfxToHsaOverride('gfx1151'), null)
|
||||
})
|
||||
|
||||
test('RDNA 2 iGPUs (gfx1031..gfx1036, e.g. 680M) coerce to 10.3.0', () => {
|
||||
assert.equal(mapGfxToHsaOverride('gfx1035'), '10.3.0')
|
||||
assert.equal(mapGfxToHsaOverride('gfx1031'), '10.3.0')
|
||||
assert.equal(mapGfxToHsaOverride('gfx1036'), '10.3.0')
|
||||
})
|
||||
|
||||
test('discrete cards on the ROCm allowlist get no override', () => {
|
||||
for (const gfx of ['gfx1030', 'gfx1100', 'gfx1101', 'gfx1102']) {
|
||||
assert.equal(mapGfxToHsaOverride(gfx), null)
|
||||
}
|
||||
})
|
||||
|
||||
test('unknown / newer targets default to native discovery (no override)', () => {
|
||||
assert.equal(mapGfxToHsaOverride('gfx9999'), null)
|
||||
assert.equal(mapGfxToHsaOverride(''), null)
|
||||
})
|
||||
|
|
@ -541,7 +541,12 @@ verify_gpu_setup() {
|
|||
amd_gfx_version='gfx1034'
|
||||
elif echo "${amd_devices}" | grep -iq 'Rembrandt'; then
|
||||
amd_gfx_version='gfx1035'
|
||||
elif echo "${amd_devices}" | grep -iEq 'Phoenix1?|Phoenix2'; then
|
||||
elif echo "${amd_devices}" | grep -iEq 'Phoenix[0-9]?|Hawk Point|Radeon (780M|760M)'; then
|
||||
# Phoenix (Ryzen 7040) / Hawk Point (Ryzen 8040) — 780M & 760M are both gfx1103.
|
||||
# lspci device strings vary (Phoenix1/Phoenix2/Phoenix3, "Hawk Point", or the bare
|
||||
# "Radeon 780M Graphics" marketing name), so match all of them or the marker goes
|
||||
# missing and the 780M silently drops to CPU. Kept before the Strix branches so a
|
||||
# "Radeon 780M" string can't be miscaught. See gfx1103 regression.
|
||||
amd_gfx_version='gfx1103'
|
||||
elif echo "${amd_devices}" | grep -iEq 'Strix Halo'; then
|
||||
amd_gfx_version='gfx1151'
|
||||
|
|
|
|||
Loading…
Reference in New Issue