Skip to content

llm: set ROCm Tensile lib path - #15527

Open
HEETMEHTA18 wants to merge 5 commits into
ollama:mainfrom
HEETMEHTA18:fix/rocm-tensile-libpath-15452-clean
Open

HEETMEHTA18 wants to merge 5 commits into
ollama:mainfrom
HEETMEHTA18:fix/rocm-tensile-libpath-15452-clean

Conversation

@HEETMEHTA18

Copy link
Copy Markdown

Resolve rocBLAS Tensile path from bundled ROCm libs and pass it to the runner when unset. Add tests for path detection and env injection. rocblaslt error: Cannot read "TensileLibrary_lazy_gfx1200.dat": No such file or directory #15452

Summary

Fix ROCm Tensile library path resolution for runner startup on gfx1200-class systems.

On some ROCm setups, rocBLASLt first attempts a relative lookup for TensileLibrary_lazy_gfx1200.dat, logs a "No such file
or directory" warning, and then falls back to an absolute path. Inference may still work, but startup is noisy and path
resolution is fragile.

This patch makes lookup deterministic by resolving the Tensile library directory from gpuLibs and setting env vars at runner
startup when they are not already provided.

Changes

  • In llm/server.go:
    • Add findRocmTensileLibraryPath(gpuLibs []string) string
    • Add ensureRocmTensileEnv(gpuLibs []string, extraEnvs map[string]string) map[string]string
    • Call ensureRocmTensileEnv(...) in StartRunner(...) before environment assembly
  • Inject (only when unset):
    • ROCBLAS_TENSILE_LIBPATH
    • ROCBLASLT_TENSILE_LIBPATH
    • HIPBLASLT_TENSILE_LIBPATH
  • Supported candidate layouts:
    • <lib>/rocblas/library
    • <lib>/rocm/rocblas/library
  • Do not override user/process-provided env values.

Tests

Added unit tests in llm/server_test.go for:

  • Tensile path detection from both directory layouts
  • Injection behavior when env vars are unset
  • Non-overwrite behavior when env vars are explicitly set

Why this change

This addresses path-resolution fragility behind issue #15452 and removes noisy false-negative file lookup behavior in runner
startup.

Compatibility

  • No behavior change when env vars are already set by user/system.
  • No injection when a valid Tensile directory is not found.
  • Complements packaging-side fixes (e.g. hipBLASLt kernel packaging), but does not replace them.

Closes #15452

Resolve rocBLAS Tensile path from bundled ROCm libs and pass it to the runner when unset. Add tests for path detection and env injection.
Copilot AI review requested due to automatic review settings April 12, 2026 19:32

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR makes ROCm runner startup more deterministic by locating the bundled rocBLAS Tensile library directory from gpuLibs and injecting Tensile-related env vars into the runner environment when they aren’t already provided, reducing noisy rocblaslt “Cannot read TensileLibrary…” warnings on gfx1200-class systems.

Changes:

  • Add ROCm Tensile library path detection from common bundled ROCm layouts.
  • Inject ROCBLAS*_TENSILE_LIBPATH / HIPBLASLT_TENSILE_LIBPATH into the runner env when not already set.
  • Add unit tests covering path detection and env injection / non-overwrite behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
llm/server.go Adds Tensile path detection and env injection during StartRunner.
llm/server_test.go Adds unit tests validating Tensile path detection and env injection behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread llm/server.go Outdated
Comment on lines +462 to +480
for _, key := range []string{"ROCBLAS_TENSILE_LIBPATH", "ROCBLASLT_TENSILE_LIBPATH", "HIPBLASLT_TENSILE_LIBPATH"} {
if _, ok := os.LookupEnv(key); ok {
return extraEnvs
}
if extraEnvs != nil {
if _, ok := extraEnvs[key]; ok {
return extraEnvs
}
}
}

if extraEnvs == nil {
extraEnvs = map[string]string{}
}

extraEnvs["ROCBLAS_TENSILE_LIBPATH"] = tensilePath
extraEnvs["ROCBLASLT_TENSILE_LIBPATH"] = tensilePath
extraEnvs["HIPBLASLT_TENSILE_LIBPATH"] = tensilePath

Copilot AI Apr 12, 2026

Copy link

Choose a reason for hiding this comment

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

ensureRocmTensileEnv currently returns early if any of the Tensile env vars are set in the process env or extraEnvs, which means it will not inject the remaining vars when only one is provided. If (for example) ROCBLAS_TENSILE_LIBPATH is set but ROCBLASLT_TENSILE_LIBPATH is not, the rocBLASLt startup warning can still occur. Consider setting each of the three variables independently when it is not already present (leaving any explicitly provided values untouched).

Suggested change
for _, key := range []string{"ROCBLAS_TENSILE_LIBPATH", "ROCBLASLT_TENSILE_LIBPATH", "HIPBLASLT_TENSILE_LIBPATH"} {
if _, ok := os.LookupEnv(key); ok {
return extraEnvs
}
if extraEnvs != nil {
if _, ok := extraEnvs[key]; ok {
return extraEnvs
}
}
}
if extraEnvs == nil {
extraEnvs = map[string]string{}
}
extraEnvs["ROCBLAS_TENSILE_LIBPATH"] = tensilePath
extraEnvs["ROCBLASLT_TENSILE_LIBPATH"] = tensilePath
extraEnvs["HIPBLASLT_TENSILE_LIBPATH"] = tensilePath
if extraEnvs == nil {
extraEnvs = map[string]string{}
}
for _, key := range []string{"ROCBLAS_TENSILE_LIBPATH", "ROCBLASLT_TENSILE_LIBPATH", "HIPBLASLT_TENSILE_LIBPATH"} {
if _, ok := os.LookupEnv(key); ok {
continue
}
if _, ok := extraEnvs[key]; ok {
continue
}
extraEnvs[key] = tensilePath
}

Copilot uses AI. Check for mistakes.
Comment thread llm/server_test.go Outdated
Comment on lines +74 to +83
t.Run("do not override explicit extra env", func(t *testing.T) {
in := map[string]string{"ROCBLASLT_TENSILE_LIBPATH": "/custom/path"}
envs := ensureRocmTensileEnv([]string{root}, in)
if envs["ROCBLASLT_TENSILE_LIBPATH"] != "/custom/path" {
t.Fatalf("ROCBLASLT_TENSILE_LIBPATH = %q, want /custom/path", envs["ROCBLASLT_TENSILE_LIBPATH"])
}
if _, ok := envs["ROCBLAS_TENSILE_LIBPATH"]; ok {
t.Fatalf("ROCBLAS_TENSILE_LIBPATH should not be injected when explicit env is set")
}
})

Copilot AI Apr 12, 2026

Copy link

Choose a reason for hiding this comment

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

The test case "do not override explicit extra env" asserts that no other Tensile env vars are injected when one (ROCBLASLT_TENSILE_LIBPATH) is explicitly set. If the intended behavior is to inject only the missing vars (without overriding the explicitly-set one), this test should be updated to expect injection of the other keys rather than blocking all injection.

Copilot uses AI. Check for mistakes.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread llm/server.go Outdated
Comment on lines +463 to +467
if _, ok := os.LookupEnv(key); ok {
continue
}
if extraEnvs != nil {
if _, ok := extraEnvs[key]; ok {

Copilot AI Apr 12, 2026

Copy link

Choose a reason for hiding this comment

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

ensureRocmTensileEnv treats an environment variable as “set” purely based on os.LookupEnv. If any of these vars exist but are an empty string (common in some service/unit configs), the helper will skip injection and the rocBLASLt relative lookup noise can still occur. Consider treating empty values as unset (e.g., check val != "" for both process env and extraEnvs).

Suggested change
if _, ok := os.LookupEnv(key); ok {
continue
}
if extraEnvs != nil {
if _, ok := extraEnvs[key]; ok {
if val, ok := os.LookupEnv(key); ok && val != "" {
continue
}
if extraEnvs != nil {
if val, ok := extraEnvs[key]; ok && val != "" {

Copilot uses AI. Check for mistakes.
Comment thread llm/server_test.go Outdated
Comment on lines +61 to +72
t.Run("inject when unset", func(t *testing.T) {
envs := ensureRocmTensileEnv([]string{root}, nil)
if envs["ROCBLAS_TENSILE_LIBPATH"] != tensile {
t.Fatalf("ROCBLAS_TENSILE_LIBPATH = %q, want %q", envs["ROCBLAS_TENSILE_LIBPATH"], tensile)
}
if envs["ROCBLASLT_TENSILE_LIBPATH"] != tensile {
t.Fatalf("ROCBLASLT_TENSILE_LIBPATH = %q, want %q", envs["ROCBLASLT_TENSILE_LIBPATH"], tensile)
}
if envs["HIPBLASLT_TENSILE_LIBPATH"] != tensile {
t.Fatalf("HIPBLASLT_TENSILE_LIBPATH = %q, want %q", envs["HIPBLASLT_TENSILE_LIBPATH"], tensile)
}
})

Copilot AI Apr 12, 2026

Copy link

Choose a reason for hiding this comment

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

These subtests assume ROCBLAS*_TENSILE_LIBPATH/HIPBLASLT_TENSILE_LIBPATH are unset in the parent process. On ROCm CI runners (or developer machines) they may already be set, causing the “inject when unset” assertions to fail. Make the test hermetic by explicitly clearing those env vars for the subtest (e.g., unset with cleanup, or set to empty if the helper treats empty-as-unset).

Copilot uses AI. Check for mistakes.
@slojosic-amd

slojosic-amd commented Apr 14, 2026 •

Copy link
Copy Markdown

@HEETMEHTA18 Thanks for working on this — the underlying issue (#15452) is real and worth addressing.

One thing worth noting: ROCBLASLT_TENSILE_LIBPATH does not appear to correspond to any actual ROCm library. The ROCm stack ships librocblas.so and libhipblaslt.so, but there is no librocblaslt.so. The rocblaslt name visible in the hipBLASLt source tree (e.g. https://github.com/ROCm/rocm-libraries/tree/develop/projects/hipblaslt/library/src/amd_detail/rocblaslt/src/Tensile/Logic/asm_full) is an internal implementation namespace, not a separate shared library and that's the reason why rocblaslt word can appear in error messages like these in #15452. As a result, setting ROCBLASLT_TENSILE_LIBPATH has no effect since nothing reads it.

However, the root cause of the missing TensileLibrary_lazy_gfx1200.dat error is that the hipblaslt/library/ folder was not being copied into the ollama install directory during the HIP CMake component install. A packaging-side fix that adds install(DIRECTORY ... hipblaslt ...) #14979 alongside the existing rocblas install rule would address this at the source rather than working around it at runtime so I think your PR would only add redundant env vars.

@HEETMEHTA18

Copy link
Copy Markdown
Author

@slojosic-amd Okaay ! I understood your reply.
But I agree packaging hipblaslt/library is the primary fix for Ollama’s bundled ROCm install. My intent with the
-llm change was to provide a runtime fallback for cases where ROCm assets are present but path discovery is
still brittle.

  • That said, if ROCBLASLT_TENSILE_LIBPATH is not actually consumed and the failing lookup is specifically due
    to missing hipblaslt/library, then the current PR is too broad / aimed at the wrong place. I can rework this
    into a packaging-only fix, or keep a much narrower runtime fallback only if we can point to a variable that
    is actually honored in this path.

I could re work on this pr and revert the previous made change and add the right changes into this pr.

@HEETMEHTA18

Copy link
Copy Markdown
Author

@slojosic-amd i have added the changes in the CMakeList.txt with adding the required library to install to solve the issue and reverted all the old env changes which where temporary changes which i thought it would work but now this should be working now as from the ref of #14979.

This branch has not been deployed

No deployments
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.

rocblaslt error: Cannot read "TensileLibrary_lazy_gfx1200.dat": No such file or directory

3 participants