Is DMA-BUF supported with MPS?

Hello,

I am trying to optimize my inference pipeline by using DMA-BUF for zero-copy memory access. However, when I try to run it, I encounter an error (HAILO_INVALID_OPERATION(6)) stating that using dmabuf is not supported when working with MPS (hailort_service).

I noticed in the official documentation that MPS utilizes “shared memory.” I have a couple of questions regarding this:

  1. Is the “shared memory” mentioned in the MPS documentation fundamentally different from DMA-BUF?

  2. Is it currently true that DMA-BUF cannot be used while MPS is enabled by design?

My Environment:

  • Hardware: Raspberry Pi 5 + Hailo-8L

  • SW Version: HailoRT 4.22.0

Any clarification on how memory is handled in MPS and whether DMA integration is possible would be greatly appreciated.

Thank you!

Hi!

Short answer to both questions: yes, they are different things, and yes, the limitation is by design — but it is a transport limitation, and it does not always trigger. I spent some time on this with HailoRT 4.23.0 and a Hailo-8L, so let me share what I found.

The shared memory used by MPS is POSIX shared memory. What crosses the RPC channel is a name, not a descriptor: the service opens it with BufferStorageParams::open_shared_memory(<name>) (hailort_service/hailort_rpc_service.cpp:607) and the transport is gRPC. On the client side there is an explicit memcpy in both directions (network_group_client.cpp:572-614). So MPS shares memory between host processes, and your buffer still gets copied once per direction.

DMA-BUF is a different mechanism. The fd is imported by the driver (vdma_buffer_map_dmabuf, then the HAILO_VDMA_BUFFER_MAP ioctl), so the vDMA reads your pages directly. An fd only means something inside the process that opened it — to pass it to another process you need SCM_RIGHTS over a unix socket, and gRPC does not do that. On top of that, HAILORT_SERVICE_ADDRESS lets the service run on another machine, where an fd makes no sense at all. That is where your error comes from: the check is at service/network_group_client.cpp:601 and it wants BufferType::VIEW. It sits before the shared-memory branch, so turning shared memory off with HAILO_SERVICE_SHARED_MEMORY_OFF does not help either. It is still there in 4.23, and the newer RPC stack refuses it too (“DMA_BUFFER is not supported in HRPC”), so it does not look like an oversight.

Now the part I did not expect. The error is not universal, it depends on your pipeline. Passing a dmabuf as input under MPS, HEFs with on-chip NMS (yolov8n, yolov11n from the zoo) went through fine, while HEFs with raw output tensors (a depth model, an OBB model) failed with exactly your HAILO_INVALID_OPERATION(6). That happens because the buffer reaches the hw element as DMA_BUFFER unless some element calls as_view() first, which mmaps the dmabuf (pipeline.cpp:293). So when it does work under MPS, your dmabuf was already mmapped and copied and you do not get zero-copy anyway. Your yolov8s_pose HEF most likely returns raw tensors, which puts it in the failing group ..

And now the measurement, which I think is the useful part here. 100 frames, 3 alternating rounds, yolov8n at 640x640 (1.23 MB per frame):

mode latency FPS
direct + normal buffer 8.10 ms 157.3
direct + DMA-BUF 8.16 ms 157.3
MPS + normal buffer 8.93 ms 157.0
MPS + DMA-BUF 8.95 ms 157.1

Two things there. DMA-BUF gives you nothing inside HailoRT: 8.10 vs 8.16 ms and the same FPS. The direct path already dma-maps your user pointer, so a normal buffer is reaching the NPU without a copy as well. The value of dmabuf is upstream — avoiding the copy from the camera or ISP into your buffer — and not in the hand-off to the chip.

The second one is the cost of MPS itself. It is latency proportional to the frame size: +0.83 ms at 1.23 MB, and +2.49 ms at 3.15 MB with a 1024x1024 model, so around 0.7-0.8 ms per MB, which is what a memcpy looks like. But it costs no throughput at all, 157.3 → 157.0 FPS, and 54.9 → 54.8 FPS on the bigger model. The chip is the bottleneck and the copy overlaps with the pipeline. So it is a trade-off between multi-process isolation and latency, not throughput.

One honest caveat: I measured on x86 with a PCIe Hailo-8L, not on a Pi 5, and I did not test 4.22. On a Pi 5 the memcpy will cost more than my numbers, so take them as a lower bound. But the shape should hold: if you only need one process, drop the service and use the direct path, you already have DMA into the NPU there.

Hope this helps!