npu-probe: backport mmap-leak fix + K>8192 backstop from ggml-rocket

bo_free() (munmap+close) fixes the per-tile mmap leak in rkt_npu_matmul; add
rocket_munmap_bo to librocket. rkt_matmul rejects K>8192 (int8 K-limit). Keeps
the canonical primitive in sync with the ggml-rocket backend copy in rk-llama.cpp.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
This commit is contained in:
Markus Fritsche
2026-07-14 11:32:13 +02:00
parent 8c430d48b3
commit a2244675b1
4 changed files with 51 additions and 8 deletions
+6
View File
@@ -50,6 +50,12 @@ void *rocket_mmap_bo(int fd, uint64_t mmap_offset, uint32_t size)
mmap_offset); mmap_offset);
} }
void rocket_munmap_bo(void *map, uint32_t size)
{
if (map && map != MAP_FAILED)
munmap(map, size);
}
int rocket_prep_bo(int fd, uint32_t handle, int64_t timeout_ns) int rocket_prep_bo(int fd, uint32_t handle, int64_t timeout_ns)
{ {
struct drm_rocket_prep_bo prep_bo; struct drm_rocket_prep_bo prep_bo;
+4
View File
@@ -48,6 +48,10 @@ int rocket_create_bo(int fd, uint32_t size, uint32_t *handle_out,
*/ */
void *rocket_mmap_bo(int fd, uint64_t mmap_offset, uint32_t size); void *rocket_mmap_bo(int fd, uint64_t mmap_offset, uint32_t size);
/* rocket_munmap_bo - undo rocket_mmap_bo (the VMA pins the GEM object; GEM_CLOSE
* alone does NOT free it, so every mmap must be paired with a munmap). */
void rocket_munmap_bo(void *map, uint32_t size);
/** /**
* rocket_prep_bo - take CPU ownership of a BO (wait for NPU, sync caches) * rocket_prep_bo - take CPU ownership of a BO (wait for NPU, sync caches)
* @fd: rocket device fd * @fd: rocket device fd
+7
View File
@@ -95,6 +95,13 @@ int rkt_build_matmul_regcmd_scaled(uint64_t *out, int out_capacity,
{ {
if (M == 0 || N == 0 || K == 0) if (M == 0 || N == 0 || K == 0)
return -1; return -1;
/* Hard backstop: the RK3588 int8 matmul K-limit is ~8192 (verified: correct
* through 8192, catastrophic garbage at 11008). Weights exceeding CBUF are
* streamed (reuse_weights_cbuf=0, emitted below) and are correct up to that
* K limit, so gate on K, not on bank count. Backend supports_op also caps
* this; the check here protects direct callers. */
if (K > 8192)
return -1;
struct op o = { struct op o = {
.stride = 1, .stride = 1,
+34 -8
View File
@@ -14,6 +14,17 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <time.h>
/* rocket_prep_bo timeout is an ABSOLUTE CLOCK_MONOTONIC deadline. Build one
* rel_ns in the future, for the job-completion wait so a hung NPU job returns
* an error instead of blocking forever (the driver watchdog is ~5s). */
static int64_t rkt_deadline_ns(int64_t rel_ns)
{
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return (int64_t)t.tv_sec * 1000000000LL + (int64_t)t.tv_nsec + rel_ns;
}
int rkt_build_matmul_regcmd_scaled(uint64_t *out, int out_capacity, int rkt_build_matmul_regcmd_scaled(uint64_t *out, int out_capacity,
uint32_t M, uint32_t N, uint32_t K, uint32_t M, uint32_t N, uint32_t K,
@@ -45,6 +56,18 @@ static void bo_write(int fd, struct rkt_bo *b, const void *src, unsigned n)
rocket_fini_bo(fd, b->h); rocket_fini_bo(fd, b->h);
} }
/* munmap + GEM_CLOSE a BO (safe on a zero-initialised struct). GEM_CLOSE alone
* leaks the mapping — the VMA keeps the object pinned until process exit. */
static void bo_free(int fd, struct rkt_bo *b)
{
if (b->map && b->map != MAP_FAILED)
rocket_munmap_bo(b->map, b->sz);
if (b->h)
rocket_close_bo(fd, b->h);
b->map = NULL;
b->h = 0;
}
int rkt_npu_matmul(int fd, const uint8_t *X, const uint8_t *Wc, int rkt_npu_matmul(int fd, const uint8_t *X, const uint8_t *Wc,
const int32_t *bias, uint32_t M, uint32_t N, uint32_t K, const int32_t *bias, uint32_t M, uint32_t N, uint32_t K,
uint8_t izp, uint8_t wzp, uint8_t ozp, uint8_t izp, uint8_t wzp, uint8_t ozp,
@@ -97,8 +120,11 @@ int rkt_npu_matmul(int fd, const uint8_t *X, const uint8_t *Wc,
bo_alloc(fd, &w, rkt_packed_weights_size(1, 1, K, n)) || bo_alloc(fd, &w, rkt_packed_weights_size(1, 1, K, n)) ||
bo_alloc(fd, &b, n * sizeof(int32_t)) || bo_alloc(fd, &b, n * sizeof(int32_t)) ||
bo_alloc(fd, &o, rkt_raw_output_size(1, m, n)) || bo_alloc(fd, &o, rkt_raw_output_size(1, m, n)) ||
bo_alloc(fd, &reg, 0x1000)) bo_alloc(fd, &reg, 0x1000)) {
bo_free(fd, &in); bo_free(fd, &w); bo_free(fd, &b);
bo_free(fd, &o); bo_free(fd, &reg);
goto out; goto out;
}
bo_write(fd, &in, ipk, rkt_raw_input_size(1, m, K)); bo_write(fd, &in, ipk, rkt_raw_input_size(1, m, K));
bo_write(fd, &w, wpk, rkt_packed_weights_size(1, 1, K, n)); bo_write(fd, &w, wpk, rkt_packed_weights_size(1, 1, K, n));
bo_write(fd, &b, bpk, n * sizeof(int32_t)); bo_write(fd, &b, bpk, n * sizeof(int32_t));
@@ -108,9 +134,9 @@ int rkt_npu_matmul(int fd, const uint8_t *X, const uint8_t *Wc,
rc, 4096, m, n, K, in.dma, w.dma, o.dma, izp, wzp, ozp, rc, 4096, m, n, K, in.dma, w.dma, o.dma, izp, wzp, ozp,
in_scale, wt_scale, out_scales ? out_scales[cc] : out_scale, b.dma); in_scale, wt_scale, out_scales ? out_scales[cc] : out_scale, b.dma);
if (nw < 0) { if (nw < 0) {
rocket_close_bo(fd, in.h); rocket_close_bo(fd, w.h); bo_free(fd, &in); bo_free(fd, &w);
rocket_close_bo(fd, b.h); rocket_close_bo(fd, o.h); bo_free(fd, &b); bo_free(fd, &o);
rocket_close_bo(fd, reg.h); bo_free(fd, &reg);
goto out; goto out;
} }
bo_write(fd, &reg, rc, (unsigned)nw * sizeof(uint64_t)); bo_write(fd, &reg, rc, (unsigned)nw * sizeof(uint64_t));
@@ -129,7 +155,7 @@ int rkt_npu_matmul(int fd, const uint8_t *X, const uint8_t *Wc,
job.out_bo_handle_count = 1; job.out_bo_handle_count = 1;
int sr = rocket_submit(fd, &job, 1); int sr = rocket_submit(fd, &job, 1);
int wr = sr ? -1 : rocket_prep_bo(fd, o.h, INT64_MAX); int wr = sr ? -1 : rocket_prep_bo(fd, o.h, rkt_deadline_ns(6000000000LL));
if (!sr && !wr) { if (!sr && !wr) {
uint8_t *got = malloc((size_t)m * n); uint8_t *got = malloc((size_t)m * n);
if (got) { if (got) {
@@ -140,9 +166,9 @@ int rkt_npu_matmul(int fd, const uint8_t *X, const uint8_t *Wc,
free(got); free(got);
} }
} }
rocket_close_bo(fd, in.h); rocket_close_bo(fd, w.h); bo_free(fd, &in); bo_free(fd, &w);
rocket_close_bo(fd, b.h); rocket_close_bo(fd, o.h); bo_free(fd, &b); bo_free(fd, &o);
rocket_close_bo(fd, reg.h); bo_free(fd, &reg);
if (sr || wr) if (sr || wr)
goto out; goto out;
} }