From bb91101079d3c65d8f95ebc3f13c7e8c80be4ffd Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Fri, 9 Oct 2026 08:36:01 +0300 Subject: [PATCH 1/3] meta : handle views of tensors allocated on the host A view shares the memory of its view_src, so ggml-alloc never allocates a view in the buffer of the split it lands in - the scheduler copies the source into the split and the ops that use the view read that copy. The view node itself is a noop and does not need a split of its own, but the meta backend asserted when one was left inside a meta split: - ggml_backend_meta_get_split_state() dereferenced tensor->buffer->context - the graph rebuild mapped every node with ggml_backend_meta_buffer_simple_tensor() Accept such nodes when they are views of host tensors, which also generalizes the previous s_copy_main workaround. This fixes the assert hit by KV cache views when using --split-mode tensor with partial offload. Assisted-by: pi:llama.cpp/Qwen3.8-Flash-Next --- ggml/src/ggml-backend-meta.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/ggml/src/ggml-backend-meta.cpp b/ggml/src/ggml-backend-meta.cpp index 8ed5f4ebbc2d..66f45be61fd9 100644 --- a/ggml/src/ggml-backend-meta.cpp +++ b/ggml/src/ggml-backend-meta.cpp @@ -1176,7 +1176,18 @@ static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state( return ret; } +static bool ggml_backend_meta_is_host_view(const struct ggml_tensor * tensor) { + return ggml_is_view(tensor) && ggml_backend_buffer_is_host(tensor->view_src->buffer); +} + static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(const struct ggml_tensor * tensor, bool assume_sync) { + if (!ggml_backend_buffer_is_meta(tensor->buffer)) { + GGML_ASSERT(ggml_backend_meta_is_host_view(tensor)); + + // the view is not allocated in the meta buffer, it is not split across the sub-devices + return { GGML_BACKEND_SPLIT_AXIS_NONE, {0}, {1}, 1 }; + } + ggml_backend_meta_buffer_context * buf_ctx = (ggml_backend_meta_buffer_context *) tensor->buffer->context; return ggml_backend_meta_get_split_state(buf_ctx->get_simple_tensor_container(tensor), tensor, assume_sync); } @@ -2026,9 +2037,10 @@ static enum ggml_status ggml_backend_meta_graph_compute(ggml_backend_t backend, for (int i = 0; i < cgraph->n_nodes; i++) { ggml_tensor * node = cgraph->nodes[i]; - if (node->view_src != nullptr && node->view_src->op == GGML_OP_NONE && ggml_backend_buffer_is_host(node->view_src->buffer)) { - // FIXME s_copy_main is on the CPU and its view seems to be incorrectly added to the graph nodes. - // For regular usage this doesn't matter since it's a noop but trying to call ggml_backend_meta_buffer_simple_tensor results in a crash. + if (!ggml_backend_buffer_is_meta(node->buffer)) { + GGML_ASSERT(ggml_backend_meta_is_host_view(node)); + + // keep the node as is, mapping it to a simple tensor is not possible bcj.nodes[i] = node; continue; } From dbc1bbcdc444978d5418dc084585f1ec2d5797f3 Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Fri, 9 Oct 2026 11:53:37 +0300 Subject: [PATCH 2/3] archs : re-enable sm tensor for K2 Horizon --- src/llama-arch.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/llama-arch.cpp b/src/llama-arch.cpp index 8214790640d1..5774867d9372 100644 --- a/src/llama-arch.cpp +++ b/src/llama-arch.cpp @@ -1234,7 +1234,6 @@ bool llm_arch_supports_sm_tensor(const llm_arch & arch) { case LLM_ARCH_KIMI_K3: case LLM_ARCH_GLM5_NEXT: case LLM_ARCH_QWEN3TTS: - case LLM_ARCH_K2_HORIZON: return false; default: return true; From ba2454ee6bcf27b7706f967113e22800fda1aa1c Mon Sep 17 00:00:00 2001 From: Georgi Gerganov Date: Fri, 9 Oct 2026 13:49:43 +0300 Subject: [PATCH 3/3] cont : add TODO and reference --- ggml/src/ggml-backend-meta.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ggml/src/ggml-backend-meta.cpp b/ggml/src/ggml-backend-meta.cpp index 66f45be61fd9..86748b689a8a 100644 --- a/ggml/src/ggml-backend-meta.cpp +++ b/ggml/src/ggml-backend-meta.cpp @@ -1181,6 +1181,10 @@ static bool ggml_backend_meta_is_host_view(const struct ggml_tensor * tensor) { } static struct ggml_backend_meta_split_state ggml_backend_meta_get_split_state(const struct ggml_tensor * tensor, bool assume_sync) { + // [TAG_META_HOST_VIEWS] + // TODO: technically, this check should not be needed if the backend scheduler correctly prevents assigning + // such host-buffer views to the meta backend. figure out how to update the scheduler logic to achieve that + // ref: https://github.com/ggml-org/llama.cpp/pull/30217 if (!ggml_backend_buffer_is_meta(tensor->buffer)) { GGML_ASSERT(ggml_backend_meta_is_host_view(tensor)); @@ -2038,6 +2042,7 @@ static enum ggml_status ggml_backend_meta_graph_compute(ggml_backend_t backend, for (int i = 0; i < cgraph->n_nodes; i++) { ggml_tensor * node = cgraph->nodes[i]; if (!ggml_backend_buffer_is_meta(node->buffer)) { + // [TAG_META_HOST_VIEWS] GGML_ASSERT(ggml_backend_meta_is_host_view(node)); // keep the node as is, mapping it to a simple tensor is not possible