Skip to content

Stop the NPU driver oopsing when inference is interrupted - #11

Merged
MrMati merged 1 commit into
mainfrom
rknpu-dmabuf-ref
Aug 1, 2026
Merged

Stop the NPU driver oopsing when inference is interrupted#11
MrMati merged 1 commit into
mainfrom
rknpu-dmabuf-ref

Conversation

@MrMati

@MrMati MrMati commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Ctrl-C during an rknn run takes the kernel down with BUG_ON(dmabuf->vmapping_counter) at the top of dma_buf_release(): a dma-buf hit its last reference while the kernel still had it vmapped. The register dump agrees, dma_buf_release+0xa is exactly that BUG_ON on this tree, the dma_buf's vmapping_counter is 1 and its attachment list is still non-empty, so nothing had torn the buffer down.

RKNPU_MEM_CREATE with args.handle == 0 (what librknnmrt uses, which is why rk_dma_heap_cma= sizes the pool) allocates the buffer and then gives its only reference away. dma_buf_fd() installs the file without taking a reference of its own, so the fd takes over the one rk_dma_heap_buffer_alloc() returned, and the driver goes on to dma_buf_attach() and dma_buf_vmap() a buffer it holds nothing on. Both teardown paths agree with the create path and skip the put when ->owner is set:

		rknpu_obj->dmabuf = dmabuf;
		rknpu_obj->owner = 1;

		fd = dma_buf_fd(dmabuf, O_CLOEXEC | O_RDWR);

That is safe only while userspace calls RKNPU_MEM_DESTROY before the last fd and mapping go, which is what the runtime's own teardown does. A killed process never gets there, and do_exit() tears the address space down in exit_mm() before it closes the fd table in exit_files(), so a buffer the runtime mmap()ed and whose fd it had already closed loses its last reference while the driver still has a kernel mapping on it. Had the BUG_ON not fired, rknpu_release() would have gone on to vunmap and detach a freed dma_buf.

The fix is one get_dma_buf() after the fd is installed, plus dropping that reference unconditionally in rknpu_mem_destroy_ioctl(), rknpu_release() and the create error path:

		fd = dma_buf_fd(dmabuf, O_CLOEXEC | O_RDWR);
		if (fd < 0) {
			LOG_ERROR("dmabuf fd get failed
");
			ret = -EFAULT;
			goto err_free_dma_buf;
		}

		/*
		 * dma_buf_fd() installs the file without taking a reference:
		 * the fd has taken over the one rk_dma_heap_buffer_alloc()
		 * returned. Take our own, so the attachment and the kernel
		 * mapping made below keep the buffer alive for as long as
		 * this rknpu_mem_object does.
		 */
		get_dma_buf(dmabuf);

The count is then one per rknpu_mem_object however the buffer was obtained, and the buffer outlives the driver's mapping of it no matter how the process ends. ->owner stops deciding anything but is left in place: it is documented in rknpu_mem.h, and deleting a member of a vendor struct is churn whose only effect here would be to make the patch likelier to stop applying.

Two smaller things fall out of the same change. dma_buf_attach() or dma_buf_map_attachment() failing after the fd was installed used to free the buffer out from under an fd userspace had already been given; with our own reference the error path drops ours and the fd keeps its own. And the copy_to_user() failure path now only vunmaps if something was vmapped, since without RKNPU_MEM_KERNEL_MAPPING the iosys_map is still zeroed and dma_buf_vunmap() opens with BUG_ON(iosys_map_is_null(&dmabuf->vmap_ptr)).

Nothing to do with #10; the 594 MHz clocking is not involved. Verified that both patches apply in order to drivers/rknpu at the pinned KERNEL_REF, and make check passes. Not yet run on hardware, the test is a while true; do ./yolo_detect ...; done and a Ctrl-C.


View with [code]smith
Need help on this PR? Tag @codesmith with what you need. Autofix is enabled.

Ctrl-C during an rknn run takes the kernel down:

  Internal error: Oops - BUG: 0 [#1] THUMB2
  PC is at dma_buf_release+0xa/0x58
   dma_buf_release from __dentry_kill+0x73/0xb6
   ...
   task_work_run from do_exit+0x235/0x570

which is BUG_ON(dmabuf->vmapping_counter) at the top of
dma_buf_release(): a dma-buf reached its last reference while the
kernel still had it vmapped.

RKNPU_MEM_CREATE gives away the buffer it allocates. dma_buf_fd() does
not take a reference of its own, so the fd it installs takes over the
one from rk_dma_heap_buffer_alloc(), and the driver goes on to attach
and dma_buf_vmap() a buffer it holds no reference to. The runtime's own
teardown hides this, because RKNPU_MEM_DESTROY unmaps before the fd
goes. A killed process never runs it, and do_exit() drops the mapping
in exit_mm() before it closes /dev/rknpu in exit_files().

Take a reference for the driver's own use and drop it unconditionally
on teardown, so a buffer outlives the mapping the driver has on it
however the process ends.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>
@MrMati
MrMati merged commit 7872e80 into main Aug 1, 2026
1 of 6 checks passed
@MrMati
MrMati deleted the rknpu-dmabuf-ref branch August 1, 2026 12:44
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.

1 participant