[v2] libcamera: dma_buf_allocator: Make provider priority configurable
diff mbox series

Message ID 20260917-udma-v2-1-74d4eead5465@oss.qualcomm.com
State Superseded
Headers show
Series
  • [v2] libcamera: dma_buf_allocator: Make provider priority configurable
Related show

Commit Message

Wenmeng Liu Sept. 17, 2026, 9:01 a.m. UTC
DmaBufAllocator selects a provider using a fixed priority (CMA heap,
system heap, then udmabuf) and picks the first one that can be opened.
On platforms with a small CMA region this is suboptimal: CMA is always
chosen when present even though it may be too small, while udmabuf,
backed by pageable system memory, would work.

Make the provider priority order configurable through the
'dma_buf_allocator/provider_priority' global configuration option, an
ordered list of preferred provider names. Providers not listed are tried
afterwards in the built-in order (CMA heap, system heap, then udmabuf).
When the option is absent the historical order is kept, so behaviour is
unchanged by default.

DmaBufAllocator reads this option itself. Its constructor now takes a
CameraManager reference to reach the GlobalConfiguration, following the
same pattern as the other software ISP helpers (SwStatsCpu, Benchmark,
Debayer). The callers only pass the camera manager and the providers they
accept, rather than parsing the configuration themselves. This lets a
platform prefer udmabuf by shipping a configuration.yaml.

The set of providers stays limited to what the caller requested, so a
component that requires physically-contiguous memory (CMA only) is not
given a udmabuf-backed buffer.

Document the new option in runtime_configuration.rst.

Signed-off-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
---
Changes in v2:
- Move the configuration parsing from the callers into
DmaBufAllocator's constructor. The constructor now takes a
'const CameraManager &cm' argument to reach the GlobalConfiguration
and reads 'dma_buf_allocator/provider_priority' itself, so the
callers no longer parse the config file. (Hans)
- Update the DmaBufAllocator callers (software ISP, virtual pipeline
and rpi/vc4) to the new constructor signature.
- Drop the "This is useful on platforms with a small CMA region..."
sentence from runtime_configuration.rst; the rationale is kept in
the commit message only. (Hans)
- Reword the commit message to describe reading the option inside
DmaBufAllocator rather than in each caller.
- Link to v1: https://lists.libcamera.org/pipermail/libcamera-devel/2026-September/062007.html
---
 Documentation/runtime_configuration.rst        | 27 +++++++
 include/libcamera/internal/dma_buf_allocator.h |  4 +-
 src/libcamera/dma_buf_allocator.cpp            | 98 +++++++++++++++++++++-----
 src/libcamera/pipeline/rpi/vc4/vc4.cpp         |  2 +-
 src/libcamera/pipeline/virtual/virtual.cpp     |  7 +-
 src/libcamera/software_isp/software_isp.cpp    |  8 ++-
 6 files changed, 121 insertions(+), 25 deletions(-)


---
base-commit: 87c7285663aaad7608fdc18d5216ec6811c685c7
change-id: 20260911-udma-a002f890212c

Best regards,

Comments

Laurent Pinchart Sept. 20, 2026, 6:36 p.m. UTC | #1
Hi Wenmeng,

Thank you for the patch.

On Thu, Sep 17, 2026 at 05:01:05PM +0800, Wenmeng Liu wrote:
> DmaBufAllocator selects a provider using a fixed priority (CMA heap,
> system heap, then udmabuf) and picks the first one that can be opened.
> On platforms with a small CMA region this is suboptimal: CMA is always
> chosen when present even though it may be too small, while udmabuf,
> backed by pageable system memory, would work.
> 
> Make the provider priority order configurable through the
> 'dma_buf_allocator/provider_priority' global configuration option, an
> ordered list of preferred provider names. Providers not listed are tried
> afterwards in the built-in order (CMA heap, system heap, then udmabuf).
> When the option is absent the historical order is kept, so behaviour is
> unchanged by default.
> 
> DmaBufAllocator reads this option itself. Its constructor now takes a
> CameraManager reference to reach the GlobalConfiguration, following the
> same pattern as the other software ISP helpers (SwStatsCpu, Benchmark,
> Debayer). The callers only pass the camera manager and the providers they
> accept, rather than parsing the configuration themselves. This lets a
> platform prefer udmabuf by shipping a configuration.yaml.
> 
> The set of providers stays limited to what the caller requested, so a
> component that requires physically-contiguous memory (CMA only) is not
> given a udmabuf-backed buffer.
> 
> Document the new option in runtime_configuration.rst.

I don't think this is a good solution. At best it's a dirty hack.

I've long said we need a way to figure out in userspace how to allocate
memory. I don't want to see more dirty hacks shoved in libcamera. This
has to stop at some point, and I think the point is now.

Buffer allocation has to move out of libcamera entirely. I don't want to
merge this patch with a concrete and actionable plan to drop the
FrameBufferAllocator completely and only import buffers in libcamera.

> Signed-off-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
> ---
> Changes in v2:
> - Move the configuration parsing from the callers into
> DmaBufAllocator's constructor. The constructor now takes a
> 'const CameraManager &cm' argument to reach the GlobalConfiguration
> and reads 'dma_buf_allocator/provider_priority' itself, so the
> callers no longer parse the config file. (Hans)
> - Update the DmaBufAllocator callers (software ISP, virtual pipeline
> and rpi/vc4) to the new constructor signature.
> - Drop the "This is useful on platforms with a small CMA region..."
> sentence from runtime_configuration.rst; the rationale is kept in
> the commit message only. (Hans)
> - Reword the commit message to describe reading the option inside
> DmaBufAllocator rather than in each caller.
> - Link to v1: https://lists.libcamera.org/pipermail/libcamera-devel/2026-September/062007.html
> ---
>  Documentation/runtime_configuration.rst        | 27 +++++++
>  include/libcamera/internal/dma_buf_allocator.h |  4 +-
>  src/libcamera/dma_buf_allocator.cpp            | 98 +++++++++++++++++++++-----
>  src/libcamera/pipeline/rpi/vc4/vc4.cpp         |  2 +-
>  src/libcamera/pipeline/virtual/virtual.cpp     |  7 +-
>  src/libcamera/software_isp/software_isp.cpp    |  8 ++-
>  6 files changed, 121 insertions(+), 25 deletions(-)
> 
> diff --git a/Documentation/runtime_configuration.rst b/Documentation/runtime_configuration.rst
> index a71b715a17245fc5fea405165323a607827a9ecb..79c6e4026adcdbb974f7398419b37f246717b9a9 100644
> --- a/Documentation/runtime_configuration.rst
> +++ b/Documentation/runtime_configuration.rst
> @@ -33,6 +33,9 @@ file structure:
>  ::
>  
>    configuration:
> +    dma_buf_allocator:
> +      provider_priority:
> +        - ... # dma-buf provider name: cma, system, or udmabuf
>      ipa:
>        force_isolation: # true/false
>        config_paths:
> @@ -62,6 +65,11 @@ Configuration file example
>     ---
>     version: 1
>     configuration:
> +     dma_buf_allocator:
> +       provider_priority:
> +         - udmabuf
> +         - cma
> +         - system
>       ipa:
>         config_paths:
>           - /home/user/.libcamera/share/ipa
> @@ -150,6 +158,25 @@ LIBCAMERA_SOFTISP_MODE, software_isp.mode
>  
>     Example value: ``gpu``
>  
> +dma_buf_allocator.provider_priority
> +   Define an ordered list of dma-buf providers to try when libcamera
> +   allocates buffers internally (for example for the software ISP). The
> +   first requested provider that is available on the system is used. Valid
> +   provider names are ``cma`` (CMA dma-heap), ``system`` (system dma-heap)
> +   and ``udmabuf`` (memfd + /dev/udmabuf). Providers accepted by a component
> +   but not listed here are tried after the listed ones, in libcamera's
> +   built-in order.
> +
> +   Example value:
> +
> +   ::
> +
> +      dma_buf_allocator:
> +        provider_priority:
> +          - udmabuf
> +          - cma
> +          - system
> +
>  pipelines.simple.supported_devices.driver, pipelines.simple.supported_devices.software_isp
>     Override whether software ISP is enabled for the given driver.
>  
> diff --git a/include/libcamera/internal/dma_buf_allocator.h b/include/libcamera/internal/dma_buf_allocator.h
> index 13600915a066d61c6f0dac2bfc7ec21d7fef33ef..07fd903170c92dc83b1b05d77aa261f6ac614c01 100644
> --- a/include/libcamera/internal/dma_buf_allocator.h
> +++ b/include/libcamera/internal/dma_buf_allocator.h
> @@ -18,6 +18,7 @@
>  
>  namespace libcamera {
>  
> +class CameraManager;
>  class FrameBuffer;
>  
>  class DmaBufAllocator
> @@ -31,7 +32,8 @@ public:
>  
>  	using DmaBufAllocatorFlags = Flags<DmaBufAllocatorFlag>;
>  
> -	DmaBufAllocator(DmaBufAllocatorFlags flags = DmaBufAllocatorFlag::CmaHeap);
> +	DmaBufAllocator(const CameraManager &cm,
> +			DmaBufAllocatorFlags type = DmaBufAllocatorFlag::CmaHeap);
>  	~DmaBufAllocator();
>  	bool isValid() const { return providerHandle_.isValid(); }
>  	UniqueFD alloc(const char *name, std::size_t size);
> diff --git a/src/libcamera/dma_buf_allocator.cpp b/src/libcamera/dma_buf_allocator.cpp
> index c673d23ec761e62b746f8984dfbdd0b01a858a38..9d96f06014eaeed38f4dda19fc4a1a4586ea488b 100644
> --- a/src/libcamera/dma_buf_allocator.cpp
> +++ b/src/libcamera/dma_buf_allocator.cpp
> @@ -8,13 +8,16 @@
>  
>  #include "libcamera/internal/dma_buf_allocator.h"
>  
> +#include <algorithm>
>  #include <array>
>  #include <fcntl.h>
> +#include <optional>
>  #include <sys/ioctl.h>
>  #include <sys/mman.h>
>  #include <sys/stat.h>
>  #include <sys/types.h>
>  #include <unistd.h>
> +#include <vector>
>  
>  #include <linux/dma-buf.h>
>  #include <linux/dma-heap.h>
> @@ -26,6 +29,8 @@
>  
>  #include <libcamera/framebuffer.h>
>  
> +#include "libcamera/internal/camera_manager.h"
> +
>  /**
>   * \file dma_buf_allocator.cpp
>   * \brief dma-buf allocator
> @@ -51,6 +56,26 @@ static constexpr std::array<DmaBufAllocatorInfo, 4> providerInfos = { {
>  	{ DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf, "/dev/udmabuf" },
>  } };
>  
> +/* Built-in provider priority, used when no other order is specified. */
> +static constexpr std::array<DmaBufAllocator::DmaBufAllocatorFlag, 3>
> +	defaultProviderPriority = { {
> +		DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap,
> +		DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap,
> +		DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf,
> +	} };
> +
> +static std::optional<DmaBufAllocator::DmaBufAllocatorFlag>
> +providerFromName(const std::string &name)
> +{
> +	if (name == "cma")
> +		return DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap;
> +	if (name == "system")
> +		return DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap;
> +	if (name == "udmabuf")
> +		return DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf;
> +	return {};
> +}
> +
>  LOG_DEFINE_CATEGORY(DmaBufAllocator)
>  
>  /**
> @@ -83,6 +108,7 @@ LOG_DEFINE_CATEGORY(DmaBufAllocator)
>  
>  /**
>   * \brief Construct a DmaBufAllocator of a given type
> + * \param[in] cm The camera manager, used to access the global configuration
>   * \param[in] type The type(s) of the dma-buf providers to allocate from
>   *
>   * The dma-buf provider type is selected with the \a type parameter, which
> @@ -90,29 +116,67 @@ LOG_DEFINE_CATEGORY(DmaBufAllocator)
>   * the constructed DmaBufAllocator instance is invalid as indicated by
>   * the isValid() function.
>   *
> - * Multiple types can be selected by combining type flags, in which case
> - * the constructed DmaBufAllocator will match one of the types. If multiple
> - * requested types can work on the system, which provider is used is undefined.
> + * Multiple types can be selected by combining type flags. In that case the
> + * provider to use is chosen by priority: the providers listed in the
> + * `dma_buf_allocator.provider_priority` global configuration option (valid
> + * names are "cma", "system" and "udmabuf") take precedence, followed by
> + * libcamera's built-in order (CMA heap, system heap, then udmabuf). The first
> + * provider that is both requested and accessible is used.
>   */
> -DmaBufAllocator::DmaBufAllocator(DmaBufAllocatorFlags type)
> +DmaBufAllocator::DmaBufAllocator(const CameraManager &cm, DmaBufAllocatorFlags type)
>  {
> -	for (const auto &info : providerInfos) {
> -		if (!(type & info.type))
> -			continue;
> +	std::vector<DmaBufAllocatorFlag> priority;
> +
> +	/*
> +	 * The global configuration can override the provider priority;
> +	 * providers not listed are appended in the built-in order, so no
> +	 * acceptable provider is ever dropped.
> +	 */
> +	const GlobalConfiguration &configuration = cm._d()->configuration();
> +	const std::vector<std::string> providerPriority =
> +		configuration.listOption({ "dma_buf_allocator", "provider_priority" })
> +			.value_or(std::vector<std::string>{});
> +
> +	for (const std::string &name : providerPriority) {
> +		auto flag = providerFromName(name);
> +		if (flag)
> +			priority.push_back(*flag);
> +		else
> +			LOG(DmaBufAllocator, Warning)
> +				<< "Ignoring unknown dma-buf provider \""
> +				<< name << "\"";
> +	}
>  
> -		int ret = ::open(info.deviceNodeName, O_RDONLY | O_CLOEXEC, 0);
> -		if (ret < 0) {
> -			ret = errno;
> -			LOG(DmaBufAllocator, Debug)
> -				<< "Failed to open " << info.deviceNodeName << ": "
> -				<< strerror(ret);
> +	for (DmaBufAllocatorFlag flag : defaultProviderPriority) {
> +		if (std::find(priority.begin(), priority.end(), flag) == priority.end())
> +			priority.push_back(flag);
> +	}
> +
> +	for (DmaBufAllocatorFlag flag : priority) {
> +		if (!(type & flag))
>  			continue;
> +
> +		for (const auto &info : providerInfos) {
> +			if (info.type != flag)
> +				continue;
> +
> +			int ret = ::open(info.deviceNodeName, O_RDONLY | O_CLOEXEC, 0);
> +			if (ret < 0) {
> +				ret = errno;
> +				LOG(DmaBufAllocator, Debug)
> +					<< "Failed to open " << info.deviceNodeName
> +					<< ": " << strerror(ret);
> +				continue;
> +			}
> +
> +			LOG(DmaBufAllocator, Debug) << "Using " << info.deviceNodeName;
> +			providerHandle_ = UniqueFD(ret);
> +			type_ = info.type;
> +			break;
>  		}
>  
> -		LOG(DmaBufAllocator, Debug) << "Using " << info.deviceNodeName;
> -		providerHandle_ = UniqueFD(ret);
> -		type_ = info.type;
> -		break;
> +		if (providerHandle_.isValid())
> +			break;
>  	}
>  
>  	if (!providerHandle_.isValid())
> diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> index 200e3b58724a9f307922aa9574cd92b5da637378..4ea0f89f700838c4f225fb1c1fac6d9e157fcfb5 100644
> --- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> +++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> @@ -39,7 +39,7 @@ class Vc4CameraData final : public RPi::CameraData
>  {
>  public:
>  	Vc4CameraData(PipelineHandler *pipe)
> -		: RPi::CameraData(pipe)
> +		: RPi::CameraData(pipe), dmaHeap_(*pipe->cameraManager())
>  	{
>  	}
>  
> diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp
> index e25dd85a7391b61bcea50e71e6264500d1e8a5b3..b8677e69a74d7fd8106022cc0c91ea8595917637 100644
> --- a/src/libcamera/pipeline/virtual/virtual.cpp
> +++ b/src/libcamera/pipeline/virtual/virtual.cpp
> @@ -238,9 +238,10 @@ bool PipelineHandlerVirtual::created_ = false;
>  
>  PipelineHandlerVirtual::PipelineHandlerVirtual(CameraManager *manager)
>  	: PipelineHandler(manager),
> -	  dmaBufAllocator_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
> -			   DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
> -			   DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
> +	  dmaBufAllocator_(*manager,
> +			   DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
> +				   DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
> +				   DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
>  {
>  }
>  
> diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
> index f8efd24424a3981fd3eca03d2fcf27723480d08c..e146a0911d9db4a61e7fa54dd643d4e308458757 100644
> --- a/src/libcamera/software_isp/software_isp.cpp
> +++ b/src/libcamera/software_isp/software_isp.cpp
> @@ -24,6 +24,7 @@
>  #include <libcamera/stream.h>
>  
>  #include "libcamera/internal/bayer_format.h"
> +#include "libcamera/internal/camera_manager.h"
>  #include "libcamera/internal/framebuffer.h"
>  #include "libcamera/internal/software_isp/debayer_params.h"
>  
> @@ -81,9 +82,10 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)
>  SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
>  			 ControlInfoMap *ipaControls)
>  	: ispWorkerThread_("SWIspWorker"),
> -	  dmaHeap_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
> -		   DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
> -		   DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
> +	  dmaHeap_(*pipe->cameraManager(),
> +		   DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
> +			   DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
> +			   DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
>  {
>  	if (!dmaHeap_.isValid()) {
>  		LOG(SoftwareIsp, Error) << "Failed to create DmaBufAllocator object";
> 
> ---
> base-commit: 87c7285663aaad7608fdc18d5216ec6811c685c7
> change-id: 20260911-udma-a002f890212c
Naushir Patuck Sept. 21, 2026, 7:45 a.m. UTC | #2
Hi Laurent,

On Sun, 20 Sept 2026 at 19:37, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Wenmeng,
>
> Thank you for the patch.
>
> On Thu, Sep 17, 2026 at 05:01:05PM +0800, Wenmeng Liu wrote:
> > DmaBufAllocator selects a provider using a fixed priority (CMA heap,
> > system heap, then udmabuf) and picks the first one that can be opened.
> > On platforms with a small CMA region this is suboptimal: CMA is always
> > chosen when present even though it may be too small, while udmabuf,
> > backed by pageable system memory, would work.
> >
> > Make the provider priority order configurable through the
> > 'dma_buf_allocator/provider_priority' global configuration option, an
> > ordered list of preferred provider names. Providers not listed are tried
> > afterwards in the built-in order (CMA heap, system heap, then udmabuf).
> > When the option is absent the historical order is kept, so behaviour is
> > unchanged by default.
> >
> > DmaBufAllocator reads this option itself. Its constructor now takes a
> > CameraManager reference to reach the GlobalConfiguration, following the
> > same pattern as the other software ISP helpers (SwStatsCpu, Benchmark,
> > Debayer). The callers only pass the camera manager and the providers they
> > accept, rather than parsing the configuration themselves. This lets a
> > platform prefer udmabuf by shipping a configuration.yaml.
> >
> > The set of providers stays limited to what the caller requested, so a
> > component that requires physically-contiguous memory (CMA only) is not
> > given a udmabuf-backed buffer.
> >
> > Document the new option in runtime_configuration.rst.
>
> I don't think this is a good solution. At best it's a dirty hack.
>
> I've long said we need a way to figure out in userspace how to allocate
> memory. I don't want to see more dirty hacks shoved in libcamera. This
> has to stop at some point, and I think the point is now.
>
> Buffer allocation has to move out of libcamera entirely. I don't want to
> merge this patch with a concrete and actionable plan to drop the
> FrameBufferAllocator completely and only import buffers in libcamera.

I think this is a reasonable stance.  However, I assume pipeline
handlers would still be ok to allocate buffers through v4l2 for purely
internal use?

Regards,
Naush


>
> > Signed-off-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
> > ---
> > Changes in v2:
> > - Move the configuration parsing from the callers into
> > DmaBufAllocator's constructor. The constructor now takes a
> > 'const CameraManager &cm' argument to reach the GlobalConfiguration
> > and reads 'dma_buf_allocator/provider_priority' itself, so the
> > callers no longer parse the config file. (Hans)
> > - Update the DmaBufAllocator callers (software ISP, virtual pipeline
> > and rpi/vc4) to the new constructor signature.
> > - Drop the "This is useful on platforms with a small CMA region..."
> > sentence from runtime_configuration.rst; the rationale is kept in
> > the commit message only. (Hans)
> > - Reword the commit message to describe reading the option inside
> > DmaBufAllocator rather than in each caller.
> > - Link to v1: https://lists.libcamera.org/pipermail/libcamera-devel/2026-September/062007.html
> > ---
> >  Documentation/runtime_configuration.rst        | 27 +++++++
> >  include/libcamera/internal/dma_buf_allocator.h |  4 +-
> >  src/libcamera/dma_buf_allocator.cpp            | 98 +++++++++++++++++++++-----
> >  src/libcamera/pipeline/rpi/vc4/vc4.cpp         |  2 +-
> >  src/libcamera/pipeline/virtual/virtual.cpp     |  7 +-
> >  src/libcamera/software_isp/software_isp.cpp    |  8 ++-
> >  6 files changed, 121 insertions(+), 25 deletions(-)
> >
> > diff --git a/Documentation/runtime_configuration.rst b/Documentation/runtime_configuration.rst
> > index a71b715a17245fc5fea405165323a607827a9ecb..79c6e4026adcdbb974f7398419b37f246717b9a9 100644
> > --- a/Documentation/runtime_configuration.rst
> > +++ b/Documentation/runtime_configuration.rst
> > @@ -33,6 +33,9 @@ file structure:
> >  ::
> >
> >    configuration:
> > +    dma_buf_allocator:
> > +      provider_priority:
> > +        - ... # dma-buf provider name: cma, system, or udmabuf
> >      ipa:
> >        force_isolation: # true/false
> >        config_paths:
> > @@ -62,6 +65,11 @@ Configuration file example
> >     ---
> >     version: 1
> >     configuration:
> > +     dma_buf_allocator:
> > +       provider_priority:
> > +         - udmabuf
> > +         - cma
> > +         - system
> >       ipa:
> >         config_paths:
> >           - /home/user/.libcamera/share/ipa
> > @@ -150,6 +158,25 @@ LIBCAMERA_SOFTISP_MODE, software_isp.mode
> >
> >     Example value: ``gpu``
> >
> > +dma_buf_allocator.provider_priority
> > +   Define an ordered list of dma-buf providers to try when libcamera
> > +   allocates buffers internally (for example for the software ISP). The
> > +   first requested provider that is available on the system is used. Valid
> > +   provider names are ``cma`` (CMA dma-heap), ``system`` (system dma-heap)
> > +   and ``udmabuf`` (memfd + /dev/udmabuf). Providers accepted by a component
> > +   but not listed here are tried after the listed ones, in libcamera's
> > +   built-in order.
> > +
> > +   Example value:
> > +
> > +   ::
> > +
> > +      dma_buf_allocator:
> > +        provider_priority:
> > +          - udmabuf
> > +          - cma
> > +          - system
> > +
> >  pipelines.simple.supported_devices.driver, pipelines.simple.supported_devices.software_isp
> >     Override whether software ISP is enabled for the given driver.
> >
> > diff --git a/include/libcamera/internal/dma_buf_allocator.h b/include/libcamera/internal/dma_buf_allocator.h
> > index 13600915a066d61c6f0dac2bfc7ec21d7fef33ef..07fd903170c92dc83b1b05d77aa261f6ac614c01 100644
> > --- a/include/libcamera/internal/dma_buf_allocator.h
> > +++ b/include/libcamera/internal/dma_buf_allocator.h
> > @@ -18,6 +18,7 @@
> >
> >  namespace libcamera {
> >
> > +class CameraManager;
> >  class FrameBuffer;
> >
> >  class DmaBufAllocator
> > @@ -31,7 +32,8 @@ public:
> >
> >       using DmaBufAllocatorFlags = Flags<DmaBufAllocatorFlag>;
> >
> > -     DmaBufAllocator(DmaBufAllocatorFlags flags = DmaBufAllocatorFlag::CmaHeap);
> > +     DmaBufAllocator(const CameraManager &cm,
> > +                     DmaBufAllocatorFlags type = DmaBufAllocatorFlag::CmaHeap);
> >       ~DmaBufAllocator();
> >       bool isValid() const { return providerHandle_.isValid(); }
> >       UniqueFD alloc(const char *name, std::size_t size);
> > diff --git a/src/libcamera/dma_buf_allocator.cpp b/src/libcamera/dma_buf_allocator.cpp
> > index c673d23ec761e62b746f8984dfbdd0b01a858a38..9d96f06014eaeed38f4dda19fc4a1a4586ea488b 100644
> > --- a/src/libcamera/dma_buf_allocator.cpp
> > +++ b/src/libcamera/dma_buf_allocator.cpp
> > @@ -8,13 +8,16 @@
> >
> >  #include "libcamera/internal/dma_buf_allocator.h"
> >
> > +#include <algorithm>
> >  #include <array>
> >  #include <fcntl.h>
> > +#include <optional>
> >  #include <sys/ioctl.h>
> >  #include <sys/mman.h>
> >  #include <sys/stat.h>
> >  #include <sys/types.h>
> >  #include <unistd.h>
> > +#include <vector>
> >
> >  #include <linux/dma-buf.h>
> >  #include <linux/dma-heap.h>
> > @@ -26,6 +29,8 @@
> >
> >  #include <libcamera/framebuffer.h>
> >
> > +#include "libcamera/internal/camera_manager.h"
> > +
> >  /**
> >   * \file dma_buf_allocator.cpp
> >   * \brief dma-buf allocator
> > @@ -51,6 +56,26 @@ static constexpr std::array<DmaBufAllocatorInfo, 4> providerInfos = { {
> >       { DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf, "/dev/udmabuf" },
> >  } };
> >
> > +/* Built-in provider priority, used when no other order is specified. */
> > +static constexpr std::array<DmaBufAllocator::DmaBufAllocatorFlag, 3>
> > +     defaultProviderPriority = { {
> > +             DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap,
> > +             DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap,
> > +             DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf,
> > +     } };
> > +
> > +static std::optional<DmaBufAllocator::DmaBufAllocatorFlag>
> > +providerFromName(const std::string &name)
> > +{
> > +     if (name == "cma")
> > +             return DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap;
> > +     if (name == "system")
> > +             return DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap;
> > +     if (name == "udmabuf")
> > +             return DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf;
> > +     return {};
> > +}
> > +
> >  LOG_DEFINE_CATEGORY(DmaBufAllocator)
> >
> >  /**
> > @@ -83,6 +108,7 @@ LOG_DEFINE_CATEGORY(DmaBufAllocator)
> >
> >  /**
> >   * \brief Construct a DmaBufAllocator of a given type
> > + * \param[in] cm The camera manager, used to access the global configuration
> >   * \param[in] type The type(s) of the dma-buf providers to allocate from
> >   *
> >   * The dma-buf provider type is selected with the \a type parameter, which
> > @@ -90,29 +116,67 @@ LOG_DEFINE_CATEGORY(DmaBufAllocator)
> >   * the constructed DmaBufAllocator instance is invalid as indicated by
> >   * the isValid() function.
> >   *
> > - * Multiple types can be selected by combining type flags, in which case
> > - * the constructed DmaBufAllocator will match one of the types. If multiple
> > - * requested types can work on the system, which provider is used is undefined.
> > + * Multiple types can be selected by combining type flags. In that case the
> > + * provider to use is chosen by priority: the providers listed in the
> > + * `dma_buf_allocator.provider_priority` global configuration option (valid
> > + * names are "cma", "system" and "udmabuf") take precedence, followed by
> > + * libcamera's built-in order (CMA heap, system heap, then udmabuf). The first
> > + * provider that is both requested and accessible is used.
> >   */
> > -DmaBufAllocator::DmaBufAllocator(DmaBufAllocatorFlags type)
> > +DmaBufAllocator::DmaBufAllocator(const CameraManager &cm, DmaBufAllocatorFlags type)
> >  {
> > -     for (const auto &info : providerInfos) {
> > -             if (!(type & info.type))
> > -                     continue;
> > +     std::vector<DmaBufAllocatorFlag> priority;
> > +
> > +     /*
> > +      * The global configuration can override the provider priority;
> > +      * providers not listed are appended in the built-in order, so no
> > +      * acceptable provider is ever dropped.
> > +      */
> > +     const GlobalConfiguration &configuration = cm._d()->configuration();
> > +     const std::vector<std::string> providerPriority =
> > +             configuration.listOption({ "dma_buf_allocator", "provider_priority" })
> > +                     .value_or(std::vector<std::string>{});
> > +
> > +     for (const std::string &name : providerPriority) {
> > +             auto flag = providerFromName(name);
> > +             if (flag)
> > +                     priority.push_back(*flag);
> > +             else
> > +                     LOG(DmaBufAllocator, Warning)
> > +                             << "Ignoring unknown dma-buf provider \""
> > +                             << name << "\"";
> > +     }
> >
> > -             int ret = ::open(info.deviceNodeName, O_RDONLY | O_CLOEXEC, 0);
> > -             if (ret < 0) {
> > -                     ret = errno;
> > -                     LOG(DmaBufAllocator, Debug)
> > -                             << "Failed to open " << info.deviceNodeName << ": "
> > -                             << strerror(ret);
> > +     for (DmaBufAllocatorFlag flag : defaultProviderPriority) {
> > +             if (std::find(priority.begin(), priority.end(), flag) == priority.end())
> > +                     priority.push_back(flag);
> > +     }
> > +
> > +     for (DmaBufAllocatorFlag flag : priority) {
> > +             if (!(type & flag))
> >                       continue;
> > +
> > +             for (const auto &info : providerInfos) {
> > +                     if (info.type != flag)
> > +                             continue;
> > +
> > +                     int ret = ::open(info.deviceNodeName, O_RDONLY | O_CLOEXEC, 0);
> > +                     if (ret < 0) {
> > +                             ret = errno;
> > +                             LOG(DmaBufAllocator, Debug)
> > +                                     << "Failed to open " << info.deviceNodeName
> > +                                     << ": " << strerror(ret);
> > +                             continue;
> > +                     }
> > +
> > +                     LOG(DmaBufAllocator, Debug) << "Using " << info.deviceNodeName;
> > +                     providerHandle_ = UniqueFD(ret);
> > +                     type_ = info.type;
> > +                     break;
> >               }
> >
> > -             LOG(DmaBufAllocator, Debug) << "Using " << info.deviceNodeName;
> > -             providerHandle_ = UniqueFD(ret);
> > -             type_ = info.type;
> > -             break;
> > +             if (providerHandle_.isValid())
> > +                     break;
> >       }
> >
> >       if (!providerHandle_.isValid())
> > diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> > index 200e3b58724a9f307922aa9574cd92b5da637378..4ea0f89f700838c4f225fb1c1fac6d9e157fcfb5 100644
> > --- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> > +++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> > @@ -39,7 +39,7 @@ class Vc4CameraData final : public RPi::CameraData
> >  {
> >  public:
> >       Vc4CameraData(PipelineHandler *pipe)
> > -             : RPi::CameraData(pipe)
> > +             : RPi::CameraData(pipe), dmaHeap_(*pipe->cameraManager())
> >       {
> >       }
> >
> > diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp
> > index e25dd85a7391b61bcea50e71e6264500d1e8a5b3..b8677e69a74d7fd8106022cc0c91ea8595917637 100644
> > --- a/src/libcamera/pipeline/virtual/virtual.cpp
> > +++ b/src/libcamera/pipeline/virtual/virtual.cpp
> > @@ -238,9 +238,10 @@ bool PipelineHandlerVirtual::created_ = false;
> >
> >  PipelineHandlerVirtual::PipelineHandlerVirtual(CameraManager *manager)
> >       : PipelineHandler(manager),
> > -       dmaBufAllocator_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
> > -                        DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
> > -                        DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
> > +       dmaBufAllocator_(*manager,
> > +                        DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
> > +                                DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
> > +                                DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
> >  {
> >  }
> >
> > diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
> > index f8efd24424a3981fd3eca03d2fcf27723480d08c..e146a0911d9db4a61e7fa54dd643d4e308458757 100644
> > --- a/src/libcamera/software_isp/software_isp.cpp
> > +++ b/src/libcamera/software_isp/software_isp.cpp
> > @@ -24,6 +24,7 @@
> >  #include <libcamera/stream.h>
> >
> >  #include "libcamera/internal/bayer_format.h"
> > +#include "libcamera/internal/camera_manager.h"
> >  #include "libcamera/internal/framebuffer.h"
> >  #include "libcamera/internal/software_isp/debayer_params.h"
> >
> > @@ -81,9 +82,10 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)
> >  SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
> >                        ControlInfoMap *ipaControls)
> >       : ispWorkerThread_("SWIspWorker"),
> > -       dmaHeap_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
> > -                DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
> > -                DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
> > +       dmaHeap_(*pipe->cameraManager(),
> > +                DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
> > +                        DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
> > +                        DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
> >  {
> >       if (!dmaHeap_.isValid()) {
> >               LOG(SoftwareIsp, Error) << "Failed to create DmaBufAllocator object";
> >
> > ---
> > base-commit: 87c7285663aaad7608fdc18d5216ec6811c685c7
> > change-id: 20260911-udma-a002f890212c
>
> --
> Regards,
>
> Laurent Pinchart
Laurent Pinchart Sept. 21, 2026, 7:50 a.m. UTC | #3
Hi Naush,

On Mon, Sep 21, 2026 at 08:45:21AM +0100, Naushir Patuck wrote:
> On Sun, 20 Sept 2026 at 19:37, Laurent Pinchart wrote:
> > On Thu, Sep 17, 2026 at 05:01:05PM +0800, Wenmeng Liu wrote:
> > > DmaBufAllocator selects a provider using a fixed priority (CMA heap,
> > > system heap, then udmabuf) and picks the first one that can be opened.
> > > On platforms with a small CMA region this is suboptimal: CMA is always
> > > chosen when present even though it may be too small, while udmabuf,
> > > backed by pageable system memory, would work.
> > >
> > > Make the provider priority order configurable through the
> > > 'dma_buf_allocator/provider_priority' global configuration option, an
> > > ordered list of preferred provider names. Providers not listed are tried
> > > afterwards in the built-in order (CMA heap, system heap, then udmabuf).
> > > When the option is absent the historical order is kept, so behaviour is
> > > unchanged by default.
> > >
> > > DmaBufAllocator reads this option itself. Its constructor now takes a
> > > CameraManager reference to reach the GlobalConfiguration, following the
> > > same pattern as the other software ISP helpers (SwStatsCpu, Benchmark,
> > > Debayer). The callers only pass the camera manager and the providers they
> > > accept, rather than parsing the configuration themselves. This lets a
> > > platform prefer udmabuf by shipping a configuration.yaml.
> > >
> > > The set of providers stays limited to what the caller requested, so a
> > > component that requires physically-contiguous memory (CMA only) is not
> > > given a udmabuf-backed buffer.
> > >
> > > Document the new option in runtime_configuration.rst.
> >
> > I don't think this is a good solution. At best it's a dirty hack.
> >
> > I've long said we need a way to figure out in userspace how to allocate
> > memory. I don't want to see more dirty hacks shoved in libcamera. This
> > has to stop at some point, and I think the point is now.
> >
> > Buffer allocation has to move out of libcamera entirely. I don't want to
> > merge this patch with a concrete and actionable plan to drop the
> > FrameBufferAllocator completely and only import buffers in libcamera.
> 
> I think this is a reasonable stance.  However, I assume pipeline
> handlers would still be ok to allocate buffers through v4l2 for purely
> internal use?

Absolutely. And it will be up to each pipeline handler to decide whether
those buffers should be allocated from V4L2, from a DMA heap, or through
any other means. Sorry for not making that clear.

> > > Signed-off-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
> > > ---
> > > Changes in v2:
> > > - Move the configuration parsing from the callers into
> > > DmaBufAllocator's constructor. The constructor now takes a
> > > 'const CameraManager &cm' argument to reach the GlobalConfiguration
> > > and reads 'dma_buf_allocator/provider_priority' itself, so the
> > > callers no longer parse the config file. (Hans)
> > > - Update the DmaBufAllocator callers (software ISP, virtual pipeline
> > > and rpi/vc4) to the new constructor signature.
> > > - Drop the "This is useful on platforms with a small CMA region..."
> > > sentence from runtime_configuration.rst; the rationale is kept in
> > > the commit message only. (Hans)
> > > - Reword the commit message to describe reading the option inside
> > > DmaBufAllocator rather than in each caller.
> > > - Link to v1: https://lists.libcamera.org/pipermail/libcamera-devel/2026-September/062007.html
> > > ---
> > >  Documentation/runtime_configuration.rst        | 27 +++++++
> > >  include/libcamera/internal/dma_buf_allocator.h |  4 +-
> > >  src/libcamera/dma_buf_allocator.cpp            | 98 +++++++++++++++++++++-----
> > >  src/libcamera/pipeline/rpi/vc4/vc4.cpp         |  2 +-
> > >  src/libcamera/pipeline/virtual/virtual.cpp     |  7 +-
> > >  src/libcamera/software_isp/software_isp.cpp    |  8 ++-
> > >  6 files changed, 121 insertions(+), 25 deletions(-)
> > >
> > > diff --git a/Documentation/runtime_configuration.rst b/Documentation/runtime_configuration.rst
> > > index a71b715a17245fc5fea405165323a607827a9ecb..79c6e4026adcdbb974f7398419b37f246717b9a9 100644
> > > --- a/Documentation/runtime_configuration.rst
> > > +++ b/Documentation/runtime_configuration.rst
> > > @@ -33,6 +33,9 @@ file structure:
> > >  ::
> > >
> > >    configuration:
> > > +    dma_buf_allocator:
> > > +      provider_priority:
> > > +        - ... # dma-buf provider name: cma, system, or udmabuf
> > >      ipa:
> > >        force_isolation: # true/false
> > >        config_paths:
> > > @@ -62,6 +65,11 @@ Configuration file example
> > >     ---
> > >     version: 1
> > >     configuration:
> > > +     dma_buf_allocator:
> > > +       provider_priority:
> > > +         - udmabuf
> > > +         - cma
> > > +         - system
> > >       ipa:
> > >         config_paths:
> > >           - /home/user/.libcamera/share/ipa
> > > @@ -150,6 +158,25 @@ LIBCAMERA_SOFTISP_MODE, software_isp.mode
> > >
> > >     Example value: ``gpu``
> > >
> > > +dma_buf_allocator.provider_priority
> > > +   Define an ordered list of dma-buf providers to try when libcamera
> > > +   allocates buffers internally (for example for the software ISP). The
> > > +   first requested provider that is available on the system is used. Valid
> > > +   provider names are ``cma`` (CMA dma-heap), ``system`` (system dma-heap)
> > > +   and ``udmabuf`` (memfd + /dev/udmabuf). Providers accepted by a component
> > > +   but not listed here are tried after the listed ones, in libcamera's
> > > +   built-in order.
> > > +
> > > +   Example value:
> > > +
> > > +   ::
> > > +
> > > +      dma_buf_allocator:
> > > +        provider_priority:
> > > +          - udmabuf
> > > +          - cma
> > > +          - system
> > > +
> > >  pipelines.simple.supported_devices.driver, pipelines.simple.supported_devices.software_isp
> > >     Override whether software ISP is enabled for the given driver.
> > >
> > > diff --git a/include/libcamera/internal/dma_buf_allocator.h b/include/libcamera/internal/dma_buf_allocator.h
> > > index 13600915a066d61c6f0dac2bfc7ec21d7fef33ef..07fd903170c92dc83b1b05d77aa261f6ac614c01 100644
> > > --- a/include/libcamera/internal/dma_buf_allocator.h
> > > +++ b/include/libcamera/internal/dma_buf_allocator.h
> > > @@ -18,6 +18,7 @@
> > >
> > >  namespace libcamera {
> > >
> > > +class CameraManager;
> > >  class FrameBuffer;
> > >
> > >  class DmaBufAllocator
> > > @@ -31,7 +32,8 @@ public:
> > >
> > >       using DmaBufAllocatorFlags = Flags<DmaBufAllocatorFlag>;
> > >
> > > -     DmaBufAllocator(DmaBufAllocatorFlags flags = DmaBufAllocatorFlag::CmaHeap);
> > > +     DmaBufAllocator(const CameraManager &cm,
> > > +                     DmaBufAllocatorFlags type = DmaBufAllocatorFlag::CmaHeap);
> > >       ~DmaBufAllocator();
> > >       bool isValid() const { return providerHandle_.isValid(); }
> > >       UniqueFD alloc(const char *name, std::size_t size);
> > > diff --git a/src/libcamera/dma_buf_allocator.cpp b/src/libcamera/dma_buf_allocator.cpp
> > > index c673d23ec761e62b746f8984dfbdd0b01a858a38..9d96f06014eaeed38f4dda19fc4a1a4586ea488b 100644
> > > --- a/src/libcamera/dma_buf_allocator.cpp
> > > +++ b/src/libcamera/dma_buf_allocator.cpp
> > > @@ -8,13 +8,16 @@
> > >
> > >  #include "libcamera/internal/dma_buf_allocator.h"
> > >
> > > +#include <algorithm>
> > >  #include <array>
> > >  #include <fcntl.h>
> > > +#include <optional>
> > >  #include <sys/ioctl.h>
> > >  #include <sys/mman.h>
> > >  #include <sys/stat.h>
> > >  #include <sys/types.h>
> > >  #include <unistd.h>
> > > +#include <vector>
> > >
> > >  #include <linux/dma-buf.h>
> > >  #include <linux/dma-heap.h>
> > > @@ -26,6 +29,8 @@
> > >
> > >  #include <libcamera/framebuffer.h>
> > >
> > > +#include "libcamera/internal/camera_manager.h"
> > > +
> > >  /**
> > >   * \file dma_buf_allocator.cpp
> > >   * \brief dma-buf allocator
> > > @@ -51,6 +56,26 @@ static constexpr std::array<DmaBufAllocatorInfo, 4> providerInfos = { {
> > >       { DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf, "/dev/udmabuf" },
> > >  } };
> > >
> > > +/* Built-in provider priority, used when no other order is specified. */
> > > +static constexpr std::array<DmaBufAllocator::DmaBufAllocatorFlag, 3>
> > > +     defaultProviderPriority = { {
> > > +             DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap,
> > > +             DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap,
> > > +             DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf,
> > > +     } };
> > > +
> > > +static std::optional<DmaBufAllocator::DmaBufAllocatorFlag>
> > > +providerFromName(const std::string &name)
> > > +{
> > > +     if (name == "cma")
> > > +             return DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap;
> > > +     if (name == "system")
> > > +             return DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap;
> > > +     if (name == "udmabuf")
> > > +             return DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf;
> > > +     return {};
> > > +}
> > > +
> > >  LOG_DEFINE_CATEGORY(DmaBufAllocator)
> > >
> > >  /**
> > > @@ -83,6 +108,7 @@ LOG_DEFINE_CATEGORY(DmaBufAllocator)
> > >
> > >  /**
> > >   * \brief Construct a DmaBufAllocator of a given type
> > > + * \param[in] cm The camera manager, used to access the global configuration
> > >   * \param[in] type The type(s) of the dma-buf providers to allocate from
> > >   *
> > >   * The dma-buf provider type is selected with the \a type parameter, which
> > > @@ -90,29 +116,67 @@ LOG_DEFINE_CATEGORY(DmaBufAllocator)
> > >   * the constructed DmaBufAllocator instance is invalid as indicated by
> > >   * the isValid() function.
> > >   *
> > > - * Multiple types can be selected by combining type flags, in which case
> > > - * the constructed DmaBufAllocator will match one of the types. If multiple
> > > - * requested types can work on the system, which provider is used is undefined.
> > > + * Multiple types can be selected by combining type flags. In that case the
> > > + * provider to use is chosen by priority: the providers listed in the
> > > + * `dma_buf_allocator.provider_priority` global configuration option (valid
> > > + * names are "cma", "system" and "udmabuf") take precedence, followed by
> > > + * libcamera's built-in order (CMA heap, system heap, then udmabuf). The first
> > > + * provider that is both requested and accessible is used.
> > >   */
> > > -DmaBufAllocator::DmaBufAllocator(DmaBufAllocatorFlags type)
> > > +DmaBufAllocator::DmaBufAllocator(const CameraManager &cm, DmaBufAllocatorFlags type)
> > >  {
> > > -     for (const auto &info : providerInfos) {
> > > -             if (!(type & info.type))
> > > -                     continue;
> > > +     std::vector<DmaBufAllocatorFlag> priority;
> > > +
> > > +     /*
> > > +      * The global configuration can override the provider priority;
> > > +      * providers not listed are appended in the built-in order, so no
> > > +      * acceptable provider is ever dropped.
> > > +      */
> > > +     const GlobalConfiguration &configuration = cm._d()->configuration();
> > > +     const std::vector<std::string> providerPriority =
> > > +             configuration.listOption({ "dma_buf_allocator", "provider_priority" })
> > > +                     .value_or(std::vector<std::string>{});
> > > +
> > > +     for (const std::string &name : providerPriority) {
> > > +             auto flag = providerFromName(name);
> > > +             if (flag)
> > > +                     priority.push_back(*flag);
> > > +             else
> > > +                     LOG(DmaBufAllocator, Warning)
> > > +                             << "Ignoring unknown dma-buf provider \""
> > > +                             << name << "\"";
> > > +     }
> > >
> > > -             int ret = ::open(info.deviceNodeName, O_RDONLY | O_CLOEXEC, 0);
> > > -             if (ret < 0) {
> > > -                     ret = errno;
> > > -                     LOG(DmaBufAllocator, Debug)
> > > -                             << "Failed to open " << info.deviceNodeName << ": "
> > > -                             << strerror(ret);
> > > +     for (DmaBufAllocatorFlag flag : defaultProviderPriority) {
> > > +             if (std::find(priority.begin(), priority.end(), flag) == priority.end())
> > > +                     priority.push_back(flag);
> > > +     }
> > > +
> > > +     for (DmaBufAllocatorFlag flag : priority) {
> > > +             if (!(type & flag))
> > >                       continue;
> > > +
> > > +             for (const auto &info : providerInfos) {
> > > +                     if (info.type != flag)
> > > +                             continue;
> > > +
> > > +                     int ret = ::open(info.deviceNodeName, O_RDONLY | O_CLOEXEC, 0);
> > > +                     if (ret < 0) {
> > > +                             ret = errno;
> > > +                             LOG(DmaBufAllocator, Debug)
> > > +                                     << "Failed to open " << info.deviceNodeName
> > > +                                     << ": " << strerror(ret);
> > > +                             continue;
> > > +                     }
> > > +
> > > +                     LOG(DmaBufAllocator, Debug) << "Using " << info.deviceNodeName;
> > > +                     providerHandle_ = UniqueFD(ret);
> > > +                     type_ = info.type;
> > > +                     break;
> > >               }
> > >
> > > -             LOG(DmaBufAllocator, Debug) << "Using " << info.deviceNodeName;
> > > -             providerHandle_ = UniqueFD(ret);
> > > -             type_ = info.type;
> > > -             break;
> > > +             if (providerHandle_.isValid())
> > > +                     break;
> > >       }
> > >
> > >       if (!providerHandle_.isValid())
> > > diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> > > index 200e3b58724a9f307922aa9574cd92b5da637378..4ea0f89f700838c4f225fb1c1fac6d9e157fcfb5 100644
> > > --- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> > > +++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
> > > @@ -39,7 +39,7 @@ class Vc4CameraData final : public RPi::CameraData
> > >  {
> > >  public:
> > >       Vc4CameraData(PipelineHandler *pipe)
> > > -             : RPi::CameraData(pipe)
> > > +             : RPi::CameraData(pipe), dmaHeap_(*pipe->cameraManager())
> > >       {
> > >       }
> > >
> > > diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp
> > > index e25dd85a7391b61bcea50e71e6264500d1e8a5b3..b8677e69a74d7fd8106022cc0c91ea8595917637 100644
> > > --- a/src/libcamera/pipeline/virtual/virtual.cpp
> > > +++ b/src/libcamera/pipeline/virtual/virtual.cpp
> > > @@ -238,9 +238,10 @@ bool PipelineHandlerVirtual::created_ = false;
> > >
> > >  PipelineHandlerVirtual::PipelineHandlerVirtual(CameraManager *manager)
> > >       : PipelineHandler(manager),
> > > -       dmaBufAllocator_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
> > > -                        DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
> > > -                        DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
> > > +       dmaBufAllocator_(*manager,
> > > +                        DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
> > > +                                DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
> > > +                                DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
> > >  {
> > >  }
> > >
> > > diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
> > > index f8efd24424a3981fd3eca03d2fcf27723480d08c..e146a0911d9db4a61e7fa54dd643d4e308458757 100644
> > > --- a/src/libcamera/software_isp/software_isp.cpp
> > > +++ b/src/libcamera/software_isp/software_isp.cpp
> > > @@ -24,6 +24,7 @@
> > >  #include <libcamera/stream.h>
> > >
> > >  #include "libcamera/internal/bayer_format.h"
> > > +#include "libcamera/internal/camera_manager.h"
> > >  #include "libcamera/internal/framebuffer.h"
> > >  #include "libcamera/internal/software_isp/debayer_params.h"
> > >
> > > @@ -81,9 +82,10 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)
> > >  SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
> > >                        ControlInfoMap *ipaControls)
> > >       : ispWorkerThread_("SWIspWorker"),
> > > -       dmaHeap_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
> > > -                DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
> > > -                DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
> > > +       dmaHeap_(*pipe->cameraManager(),
> > > +                DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
> > > +                        DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
> > > +                        DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
> > >  {
> > >       if (!dmaHeap_.isValid()) {
> > >               LOG(SoftwareIsp, Error) << "Failed to create DmaBufAllocator object";
> > >
> > > ---
> > > base-commit: 87c7285663aaad7608fdc18d5216ec6811c685c7
> > > change-id: 20260911-udma-a002f890212c
Hans de Goede Sept. 21, 2026, 11:21 a.m. UTC | #4
Hi,

On 21-Sep-26 9:50 AM, Laurent Pinchart wrote:
> Hi Naush,
> 
> On Mon, Sep 21, 2026 at 08:45:21AM +0100, Naushir Patuck wrote:
>> On Sun, 20 Sept 2026 at 19:37, Laurent Pinchart wrote:
>>> On Thu, Sep 17, 2026 at 05:01:05PM +0800, Wenmeng Liu wrote:
>>>> DmaBufAllocator selects a provider using a fixed priority (CMA heap,
>>>> system heap, then udmabuf) and picks the first one that can be opened.
>>>> On platforms with a small CMA region this is suboptimal: CMA is always
>>>> chosen when present even though it may be too small, while udmabuf,
>>>> backed by pageable system memory, would work.
>>>>
>>>> Make the provider priority order configurable through the
>>>> 'dma_buf_allocator/provider_priority' global configuration option, an
>>>> ordered list of preferred provider names. Providers not listed are tried
>>>> afterwards in the built-in order (CMA heap, system heap, then udmabuf).
>>>> When the option is absent the historical order is kept, so behaviour is
>>>> unchanged by default.
>>>>
>>>> DmaBufAllocator reads this option itself. Its constructor now takes a
>>>> CameraManager reference to reach the GlobalConfiguration, following the
>>>> same pattern as the other software ISP helpers (SwStatsCpu, Benchmark,
>>>> Debayer). The callers only pass the camera manager and the providers they
>>>> accept, rather than parsing the configuration themselves. This lets a
>>>> platform prefer udmabuf by shipping a configuration.yaml.
>>>>
>>>> The set of providers stays limited to what the caller requested, so a
>>>> component that requires physically-contiguous memory (CMA only) is not
>>>> given a udmabuf-backed buffer.
>>>>
>>>> Document the new option in runtime_configuration.rst.
>>>
>>> I don't think this is a good solution. At best it's a dirty hack.
>>>
>>> I've long said we need a way to figure out in userspace how to allocate
>>> memory. I don't want to see more dirty hacks shoved in libcamera. This
>>> has to stop at some point, and I think the point is now.
>>>
>>> Buffer allocation has to move out of libcamera entirely. I don't want to
>>> merge this patch with a concrete and actionable plan to drop the
>>> FrameBufferAllocator completely and only import buffers in libcamera.
>>
>> I think this is a reasonable stance.  However, I assume pipeline
>> handlers would still be ok to allocate buffers through v4l2 for purely
>> internal use?
> 
> Absolutely. And it will be up to each pipeline handler to decide whether
> those buffers should be allocated from V4L2, from a DMA heap, or through
> any other means. Sorry for not making that clear.

Right, so this mostly is about allowing pipeline-handlers to not only
specify which sources/backends dma_buf_allocator should try, but also
in which order to try them.

I suggested to Wenmeng in the downstream issue tracking this to make
this configurable through the config file, as an override to any
pipeline-handler provided order.

This patch only provides the configfile override and is missing
the bits for the pipeline-handler(s) to specify the order.

ATM users of the DmaBufAllocator class, specify which backends
are acceptable to use by passing a bitmask with 1 bit per
acceptable backend.

How if we change the flags argument to the constructor:

DmaBufAllocator(DmaBufAllocatorFlags flags);

to a vector:

DmaBufAllocator(const std::vector<DmaBufAllocatorType> types);

and then have the constructor try the supported allocator types
in the order in which they are in the vector?

This way pipeline-handlers can specify an order of preference
in which to try the different backends and everything else
stays the same.

Regards,

Hans








> 
>>>> Signed-off-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
>>>> ---
>>>> Changes in v2:
>>>> - Move the configuration parsing from the callers into
>>>> DmaBufAllocator's constructor. The constructor now takes a
>>>> 'const CameraManager &cm' argument to reach the GlobalConfiguration
>>>> and reads 'dma_buf_allocator/provider_priority' itself, so the
>>>> callers no longer parse the config file. (Hans)
>>>> - Update the DmaBufAllocator callers (software ISP, virtual pipeline
>>>> and rpi/vc4) to the new constructor signature.
>>>> - Drop the "This is useful on platforms with a small CMA region..."
>>>> sentence from runtime_configuration.rst; the rationale is kept in
>>>> the commit message only. (Hans)
>>>> - Reword the commit message to describe reading the option inside
>>>> DmaBufAllocator rather than in each caller.
>>>> - Link to v1: https://lists.libcamera.org/pipermail/libcamera-devel/2026-September/062007.html
>>>> ---
>>>>  Documentation/runtime_configuration.rst        | 27 +++++++
>>>>  include/libcamera/internal/dma_buf_allocator.h |  4 +-
>>>>  src/libcamera/dma_buf_allocator.cpp            | 98 +++++++++++++++++++++-----
>>>>  src/libcamera/pipeline/rpi/vc4/vc4.cpp         |  2 +-
>>>>  src/libcamera/pipeline/virtual/virtual.cpp     |  7 +-
>>>>  src/libcamera/software_isp/software_isp.cpp    |  8 ++-
>>>>  6 files changed, 121 insertions(+), 25 deletions(-)
>>>>
>>>> diff --git a/Documentation/runtime_configuration.rst b/Documentation/runtime_configuration.rst
>>>> index a71b715a17245fc5fea405165323a607827a9ecb..79c6e4026adcdbb974f7398419b37f246717b9a9 100644
>>>> --- a/Documentation/runtime_configuration.rst
>>>> +++ b/Documentation/runtime_configuration.rst
>>>> @@ -33,6 +33,9 @@ file structure:
>>>>  ::
>>>>
>>>>    configuration:
>>>> +    dma_buf_allocator:
>>>> +      provider_priority:
>>>> +        - ... # dma-buf provider name: cma, system, or udmabuf
>>>>      ipa:
>>>>        force_isolation: # true/false
>>>>        config_paths:
>>>> @@ -62,6 +65,11 @@ Configuration file example
>>>>     ---
>>>>     version: 1
>>>>     configuration:
>>>> +     dma_buf_allocator:
>>>> +       provider_priority:
>>>> +         - udmabuf
>>>> +         - cma
>>>> +         - system
>>>>       ipa:
>>>>         config_paths:
>>>>           - /home/user/.libcamera/share/ipa
>>>> @@ -150,6 +158,25 @@ LIBCAMERA_SOFTISP_MODE, software_isp.mode
>>>>
>>>>     Example value: ``gpu``
>>>>
>>>> +dma_buf_allocator.provider_priority
>>>> +   Define an ordered list of dma-buf providers to try when libcamera
>>>> +   allocates buffers internally (for example for the software ISP). The
>>>> +   first requested provider that is available on the system is used. Valid
>>>> +   provider names are ``cma`` (CMA dma-heap), ``system`` (system dma-heap)
>>>> +   and ``udmabuf`` (memfd + /dev/udmabuf). Providers accepted by a component
>>>> +   but not listed here are tried after the listed ones, in libcamera's
>>>> +   built-in order.
>>>> +
>>>> +   Example value:
>>>> +
>>>> +   ::
>>>> +
>>>> +      dma_buf_allocator:
>>>> +        provider_priority:
>>>> +          - udmabuf
>>>> +          - cma
>>>> +          - system
>>>> +
>>>>  pipelines.simple.supported_devices.driver, pipelines.simple.supported_devices.software_isp
>>>>     Override whether software ISP is enabled for the given driver.
>>>>
>>>> diff --git a/include/libcamera/internal/dma_buf_allocator.h b/include/libcamera/internal/dma_buf_allocator.h
>>>> index 13600915a066d61c6f0dac2bfc7ec21d7fef33ef..07fd903170c92dc83b1b05d77aa261f6ac614c01 100644
>>>> --- a/include/libcamera/internal/dma_buf_allocator.h
>>>> +++ b/include/libcamera/internal/dma_buf_allocator.h
>>>> @@ -18,6 +18,7 @@
>>>>
>>>>  namespace libcamera {
>>>>
>>>> +class CameraManager;
>>>>  class FrameBuffer;
>>>>
>>>>  class DmaBufAllocator
>>>> @@ -31,7 +32,8 @@ public:
>>>>
>>>>       using DmaBufAllocatorFlags = Flags<DmaBufAllocatorFlag>;
>>>>
>>>> -     DmaBufAllocator(DmaBufAllocatorFlags flags = DmaBufAllocatorFlag::CmaHeap);
>>>> +     DmaBufAllocator(const CameraManager &cm,
>>>> +                     DmaBufAllocatorFlags type = DmaBufAllocatorFlag::CmaHeap);
>>>>       ~DmaBufAllocator();
>>>>       bool isValid() const { return providerHandle_.isValid(); }
>>>>       UniqueFD alloc(const char *name, std::size_t size);
>>>> diff --git a/src/libcamera/dma_buf_allocator.cpp b/src/libcamera/dma_buf_allocator.cpp
>>>> index c673d23ec761e62b746f8984dfbdd0b01a858a38..9d96f06014eaeed38f4dda19fc4a1a4586ea488b 100644
>>>> --- a/src/libcamera/dma_buf_allocator.cpp
>>>> +++ b/src/libcamera/dma_buf_allocator.cpp
>>>> @@ -8,13 +8,16 @@
>>>>
>>>>  #include "libcamera/internal/dma_buf_allocator.h"
>>>>
>>>> +#include <algorithm>
>>>>  #include <array>
>>>>  #include <fcntl.h>
>>>> +#include <optional>
>>>>  #include <sys/ioctl.h>
>>>>  #include <sys/mman.h>
>>>>  #include <sys/stat.h>
>>>>  #include <sys/types.h>
>>>>  #include <unistd.h>
>>>> +#include <vector>
>>>>
>>>>  #include <linux/dma-buf.h>
>>>>  #include <linux/dma-heap.h>
>>>> @@ -26,6 +29,8 @@
>>>>
>>>>  #include <libcamera/framebuffer.h>
>>>>
>>>> +#include "libcamera/internal/camera_manager.h"
>>>> +
>>>>  /**
>>>>   * \file dma_buf_allocator.cpp
>>>>   * \brief dma-buf allocator
>>>> @@ -51,6 +56,26 @@ static constexpr std::array<DmaBufAllocatorInfo, 4> providerInfos = { {
>>>>       { DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf, "/dev/udmabuf" },
>>>>  } };
>>>>
>>>> +/* Built-in provider priority, used when no other order is specified. */
>>>> +static constexpr std::array<DmaBufAllocator::DmaBufAllocatorFlag, 3>
>>>> +     defaultProviderPriority = { {
>>>> +             DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap,
>>>> +             DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap,
>>>> +             DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf,
>>>> +     } };
>>>> +
>>>> +static std::optional<DmaBufAllocator::DmaBufAllocatorFlag>
>>>> +providerFromName(const std::string &name)
>>>> +{
>>>> +     if (name == "cma")
>>>> +             return DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap;
>>>> +     if (name == "system")
>>>> +             return DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap;
>>>> +     if (name == "udmabuf")
>>>> +             return DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf;
>>>> +     return {};
>>>> +}
>>>> +
>>>>  LOG_DEFINE_CATEGORY(DmaBufAllocator)
>>>>
>>>>  /**
>>>> @@ -83,6 +108,7 @@ LOG_DEFINE_CATEGORY(DmaBufAllocator)
>>>>
>>>>  /**
>>>>   * \brief Construct a DmaBufAllocator of a given type
>>>> + * \param[in] cm The camera manager, used to access the global configuration
>>>>   * \param[in] type The type(s) of the dma-buf providers to allocate from
>>>>   *
>>>>   * The dma-buf provider type is selected with the \a type parameter, which
>>>> @@ -90,29 +116,67 @@ LOG_DEFINE_CATEGORY(DmaBufAllocator)
>>>>   * the constructed DmaBufAllocator instance is invalid as indicated by
>>>>   * the isValid() function.
>>>>   *
>>>> - * Multiple types can be selected by combining type flags, in which case
>>>> - * the constructed DmaBufAllocator will match one of the types. If multiple
>>>> - * requested types can work on the system, which provider is used is undefined.
>>>> + * Multiple types can be selected by combining type flags. In that case the
>>>> + * provider to use is chosen by priority: the providers listed in the
>>>> + * `dma_buf_allocator.provider_priority` global configuration option (valid
>>>> + * names are "cma", "system" and "udmabuf") take precedence, followed by
>>>> + * libcamera's built-in order (CMA heap, system heap, then udmabuf). The first
>>>> + * provider that is both requested and accessible is used.
>>>>   */
>>>> -DmaBufAllocator::DmaBufAllocator(DmaBufAllocatorFlags type)
>>>> +DmaBufAllocator::DmaBufAllocator(const CameraManager &cm, DmaBufAllocatorFlags type)
>>>>  {
>>>> -     for (const auto &info : providerInfos) {
>>>> -             if (!(type & info.type))
>>>> -                     continue;
>>>> +     std::vector<DmaBufAllocatorFlag> priority;
>>>> +
>>>> +     /*
>>>> +      * The global configuration can override the provider priority;
>>>> +      * providers not listed are appended in the built-in order, so no
>>>> +      * acceptable provider is ever dropped.
>>>> +      */
>>>> +     const GlobalConfiguration &configuration = cm._d()->configuration();
>>>> +     const std::vector<std::string> providerPriority =
>>>> +             configuration.listOption({ "dma_buf_allocator", "provider_priority" })
>>>> +                     .value_or(std::vector<std::string>{});
>>>> +
>>>> +     for (const std::string &name : providerPriority) {
>>>> +             auto flag = providerFromName(name);
>>>> +             if (flag)
>>>> +                     priority.push_back(*flag);
>>>> +             else
>>>> +                     LOG(DmaBufAllocator, Warning)
>>>> +                             << "Ignoring unknown dma-buf provider \""
>>>> +                             << name << "\"";
>>>> +     }
>>>>
>>>> -             int ret = ::open(info.deviceNodeName, O_RDONLY | O_CLOEXEC, 0);
>>>> -             if (ret < 0) {
>>>> -                     ret = errno;
>>>> -                     LOG(DmaBufAllocator, Debug)
>>>> -                             << "Failed to open " << info.deviceNodeName << ": "
>>>> -                             << strerror(ret);
>>>> +     for (DmaBufAllocatorFlag flag : defaultProviderPriority) {
>>>> +             if (std::find(priority.begin(), priority.end(), flag) == priority.end())
>>>> +                     priority.push_back(flag);
>>>> +     }
>>>> +
>>>> +     for (DmaBufAllocatorFlag flag : priority) {
>>>> +             if (!(type & flag))
>>>>                       continue;
>>>> +
>>>> +             for (const auto &info : providerInfos) {
>>>> +                     if (info.type != flag)
>>>> +                             continue;
>>>> +
>>>> +                     int ret = ::open(info.deviceNodeName, O_RDONLY | O_CLOEXEC, 0);
>>>> +                     if (ret < 0) {
>>>> +                             ret = errno;
>>>> +                             LOG(DmaBufAllocator, Debug)
>>>> +                                     << "Failed to open " << info.deviceNodeName
>>>> +                                     << ": " << strerror(ret);
>>>> +                             continue;
>>>> +                     }
>>>> +
>>>> +                     LOG(DmaBufAllocator, Debug) << "Using " << info.deviceNodeName;
>>>> +                     providerHandle_ = UniqueFD(ret);
>>>> +                     type_ = info.type;
>>>> +                     break;
>>>>               }
>>>>
>>>> -             LOG(DmaBufAllocator, Debug) << "Using " << info.deviceNodeName;
>>>> -             providerHandle_ = UniqueFD(ret);
>>>> -             type_ = info.type;
>>>> -             break;
>>>> +             if (providerHandle_.isValid())
>>>> +                     break;
>>>>       }
>>>>
>>>>       if (!providerHandle_.isValid())
>>>> diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
>>>> index 200e3b58724a9f307922aa9574cd92b5da637378..4ea0f89f700838c4f225fb1c1fac6d9e157fcfb5 100644
>>>> --- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
>>>> +++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
>>>> @@ -39,7 +39,7 @@ class Vc4CameraData final : public RPi::CameraData
>>>>  {
>>>>  public:
>>>>       Vc4CameraData(PipelineHandler *pipe)
>>>> -             : RPi::CameraData(pipe)
>>>> +             : RPi::CameraData(pipe), dmaHeap_(*pipe->cameraManager())
>>>>       {
>>>>       }
>>>>
>>>> diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp
>>>> index e25dd85a7391b61bcea50e71e6264500d1e8a5b3..b8677e69a74d7fd8106022cc0c91ea8595917637 100644
>>>> --- a/src/libcamera/pipeline/virtual/virtual.cpp
>>>> +++ b/src/libcamera/pipeline/virtual/virtual.cpp
>>>> @@ -238,9 +238,10 @@ bool PipelineHandlerVirtual::created_ = false;
>>>>
>>>>  PipelineHandlerVirtual::PipelineHandlerVirtual(CameraManager *manager)
>>>>       : PipelineHandler(manager),
>>>> -       dmaBufAllocator_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
>>>> -                        DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
>>>> -                        DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
>>>> +       dmaBufAllocator_(*manager,
>>>> +                        DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
>>>> +                                DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
>>>> +                                DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
>>>>  {
>>>>  }
>>>>
>>>> diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
>>>> index f8efd24424a3981fd3eca03d2fcf27723480d08c..e146a0911d9db4a61e7fa54dd643d4e308458757 100644
>>>> --- a/src/libcamera/software_isp/software_isp.cpp
>>>> +++ b/src/libcamera/software_isp/software_isp.cpp
>>>> @@ -24,6 +24,7 @@
>>>>  #include <libcamera/stream.h>
>>>>
>>>>  #include "libcamera/internal/bayer_format.h"
>>>> +#include "libcamera/internal/camera_manager.h"
>>>>  #include "libcamera/internal/framebuffer.h"
>>>>  #include "libcamera/internal/software_isp/debayer_params.h"
>>>>
>>>> @@ -81,9 +82,10 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)
>>>>  SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
>>>>                        ControlInfoMap *ipaControls)
>>>>       : ispWorkerThread_("SWIspWorker"),
>>>> -       dmaHeap_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
>>>> -                DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
>>>> -                DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
>>>> +       dmaHeap_(*pipe->cameraManager(),
>>>> +                DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
>>>> +                        DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
>>>> +                        DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
>>>>  {
>>>>       if (!dmaHeap_.isValid()) {
>>>>               LOG(SoftwareIsp, Error) << "Failed to create DmaBufAllocator object";
>>>>
>>>> ---
>>>> base-commit: 87c7285663aaad7608fdc18d5216ec6811c685c7
>>>> change-id: 20260911-udma-a002f890212c
>
Barnabás Pőcze Oct. 2, 2026, 1:43 p.m. UTC | #5
2026. 09. 21. 13:21 keltezéssel, johannes.goede@oss.qualcomm.com írta:
> Hi,
> 
> On 21-Sep-26 9:50 AM, Laurent Pinchart wrote:
>> Hi Naush,
>>
>> On Mon, Sep 21, 2026 at 08:45:21AM +0100, Naushir Patuck wrote:
>>> On Sun, 20 Sept 2026 at 19:37, Laurent Pinchart wrote:
>>>> On Thu, Sep 17, 2026 at 05:01:05PM +0800, Wenmeng Liu wrote:
>>>>> DmaBufAllocator selects a provider using a fixed priority (CMA heap,
>>>>> system heap, then udmabuf) and picks the first one that can be opened.
>>>>> On platforms with a small CMA region this is suboptimal: CMA is always
>>>>> chosen when present even though it may be too small, while udmabuf,
>>>>> backed by pageable system memory, would work.
>>>>>
>>>>> Make the provider priority order configurable through the
>>>>> 'dma_buf_allocator/provider_priority' global configuration option, an
>>>>> ordered list of preferred provider names. Providers not listed are tried
>>>>> afterwards in the built-in order (CMA heap, system heap, then udmabuf).
>>>>> When the option is absent the historical order is kept, so behaviour is
>>>>> unchanged by default.
>>>>>
>>>>> DmaBufAllocator reads this option itself. Its constructor now takes a
>>>>> CameraManager reference to reach the GlobalConfiguration, following the
>>>>> same pattern as the other software ISP helpers (SwStatsCpu, Benchmark,
>>>>> Debayer). The callers only pass the camera manager and the providers they
>>>>> accept, rather than parsing the configuration themselves. This lets a
>>>>> platform prefer udmabuf by shipping a configuration.yaml.
>>>>>
>>>>> The set of providers stays limited to what the caller requested, so a
>>>>> component that requires physically-contiguous memory (CMA only) is not
>>>>> given a udmabuf-backed buffer.
>>>>>
>>>>> Document the new option in runtime_configuration.rst.
>>>>
>>>> I don't think this is a good solution. At best it's a dirty hack.
>>>>
>>>> I've long said we need a way to figure out in userspace how to allocate
>>>> memory. I don't want to see more dirty hacks shoved in libcamera. This
>>>> has to stop at some point, and I think the point is now.
>>>>
>>>> Buffer allocation has to move out of libcamera entirely. I don't want to
>>>> merge this patch with a concrete and actionable plan to drop the
>>>> FrameBufferAllocator completely and only import buffers in libcamera.
>>>
>>> I think this is a reasonable stance.  However, I assume pipeline
>>> handlers would still be ok to allocate buffers through v4l2 for purely
>>> internal use?
>>
>> Absolutely. And it will be up to each pipeline handler to decide whether
>> those buffers should be allocated from V4L2, from a DMA heap, or through
>> any other means. Sorry for not making that clear.
> 
> Right, so this mostly is about allowing pipeline-handlers to not only
> specify which sources/backends dma_buf_allocator should try, but also
> in which order to try them.
> 
> I suggested to Wenmeng in the downstream issue tracking this to make
> this configurable through the config file, as an override to any
> pipeline-handler provided order.
> 
> This patch only provides the configfile override and is missing
> the bits for the pipeline-handler(s) to specify the order.
> 
> ATM users of the DmaBufAllocator class, specify which backends
> are acceptable to use by passing a bitmask with 1 bit per
> acceptable backend.
> 
> How if we change the flags argument to the constructor:
> 
> DmaBufAllocator(DmaBufAllocatorFlags flags);
> 
> to a vector:
> 
> DmaBufAllocator(const std::vector<DmaBufAllocatorType> types);
> 
> and then have the constructor try the supported allocator types
> in the order in which they are in the vector?

   std::span<const DmaBufAllocatorType> types

but otherwise I think that's a reasonable idea.


> 
> This way pipeline-handlers can specify an order of preference
> in which to try the different backends and everything else
> stays the same.

So then you wouldn't even need the configuration file changes?
If so, I think that's even better.


> 
> Regards,
> 
> Hans
> 
> 
> 
> 
> 
> 
> 
> 
>>
>>>>> Signed-off-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
>>>>> ---
>>>>> Changes in v2:
>>>>> - Move the configuration parsing from the callers into
>>>>> DmaBufAllocator's constructor. The constructor now takes a
>>>>> 'const CameraManager &cm' argument to reach the GlobalConfiguration
>>>>> and reads 'dma_buf_allocator/provider_priority' itself, so the
>>>>> callers no longer parse the config file. (Hans)
>>>>> - Update the DmaBufAllocator callers (software ISP, virtual pipeline
>>>>> and rpi/vc4) to the new constructor signature.
>>>>> - Drop the "This is useful on platforms with a small CMA region..."
>>>>> sentence from runtime_configuration.rst; the rationale is kept in
>>>>> the commit message only. (Hans)
>>>>> - Reword the commit message to describe reading the option inside
>>>>> DmaBufAllocator rather than in each caller.
>>>>> - Link to v1: https://lists.libcamera.org/pipermail/libcamera-devel/2026-September/062007.html
>>>>> ---
>>>>>   Documentation/runtime_configuration.rst        | 27 +++++++
>>>>>   include/libcamera/internal/dma_buf_allocator.h |  4 +-
>>>>>   src/libcamera/dma_buf_allocator.cpp            | 98 +++++++++++++++++++++-----
>>>>>   src/libcamera/pipeline/rpi/vc4/vc4.cpp         |  2 +-
>>>>>   src/libcamera/pipeline/virtual/virtual.cpp     |  7 +-
>>>>>   src/libcamera/software_isp/software_isp.cpp    |  8 ++-
>>>>>   6 files changed, 121 insertions(+), 25 deletions(-)
>>>>>
>>>>> diff --git a/Documentation/runtime_configuration.rst b/Documentation/runtime_configuration.rst
>>>>> index a71b715a17245fc5fea405165323a607827a9ecb..79c6e4026adcdbb974f7398419b37f246717b9a9 100644
>>>>> --- a/Documentation/runtime_configuration.rst
>>>>> +++ b/Documentation/runtime_configuration.rst
>>>>> @@ -33,6 +33,9 @@ file structure:
>>>>>   ::
>>>>>
>>>>>     configuration:
>>>>> +    dma_buf_allocator:
>>>>> +      provider_priority:
>>>>> +        - ... # dma-buf provider name: cma, system, or udmabuf
>>>>>       ipa:
>>>>>         force_isolation: # true/false
>>>>>         config_paths:
>>>>> @@ -62,6 +65,11 @@ Configuration file example
>>>>>      ---
>>>>>      version: 1
>>>>>      configuration:
>>>>> +     dma_buf_allocator:
>>>>> +       provider_priority:
>>>>> +         - udmabuf
>>>>> +         - cma
>>>>> +         - system
>>>>>        ipa:
>>>>>          config_paths:
>>>>>            - /home/user/.libcamera/share/ipa
>>>>> @@ -150,6 +158,25 @@ LIBCAMERA_SOFTISP_MODE, software_isp.mode
>>>>>
>>>>>      Example value: ``gpu``
>>>>>
>>>>> +dma_buf_allocator.provider_priority
>>>>> +   Define an ordered list of dma-buf providers to try when libcamera
>>>>> +   allocates buffers internally (for example for the software ISP). The
>>>>> +   first requested provider that is available on the system is used. Valid
>>>>> +   provider names are ``cma`` (CMA dma-heap), ``system`` (system dma-heap)
>>>>> +   and ``udmabuf`` (memfd + /dev/udmabuf). Providers accepted by a component
>>>>> +   but not listed here are tried after the listed ones, in libcamera's
>>>>> +   built-in order.
>>>>> +
>>>>> +   Example value:
>>>>> +
>>>>> +   ::
>>>>> +
>>>>> +      dma_buf_allocator:
>>>>> +        provider_priority:
>>>>> +          - udmabuf
>>>>> +          - cma
>>>>> +          - system
>>>>> +
>>>>>   pipelines.simple.supported_devices.driver, pipelines.simple.supported_devices.software_isp
>>>>>      Override whether software ISP is enabled for the given driver.
>>>>>
>>>>> diff --git a/include/libcamera/internal/dma_buf_allocator.h b/include/libcamera/internal/dma_buf_allocator.h
>>>>> index 13600915a066d61c6f0dac2bfc7ec21d7fef33ef..07fd903170c92dc83b1b05d77aa261f6ac614c01 100644
>>>>> --- a/include/libcamera/internal/dma_buf_allocator.h
>>>>> +++ b/include/libcamera/internal/dma_buf_allocator.h
>>>>> @@ -18,6 +18,7 @@
>>>>>
>>>>>   namespace libcamera {
>>>>>
>>>>> +class CameraManager;
>>>>>   class FrameBuffer;
>>>>>
>>>>>   class DmaBufAllocator
>>>>> @@ -31,7 +32,8 @@ public:
>>>>>
>>>>>        using DmaBufAllocatorFlags = Flags<DmaBufAllocatorFlag>;
>>>>>
>>>>> -     DmaBufAllocator(DmaBufAllocatorFlags flags = DmaBufAllocatorFlag::CmaHeap);
>>>>> +     DmaBufAllocator(const CameraManager &cm,
>>>>> +                     DmaBufAllocatorFlags type = DmaBufAllocatorFlag::CmaHeap);
>>>>>        ~DmaBufAllocator();
>>>>>        bool isValid() const { return providerHandle_.isValid(); }
>>>>>        UniqueFD alloc(const char *name, std::size_t size);
>>>>> diff --git a/src/libcamera/dma_buf_allocator.cpp b/src/libcamera/dma_buf_allocator.cpp
>>>>> index c673d23ec761e62b746f8984dfbdd0b01a858a38..9d96f06014eaeed38f4dda19fc4a1a4586ea488b 100644
>>>>> --- a/src/libcamera/dma_buf_allocator.cpp
>>>>> +++ b/src/libcamera/dma_buf_allocator.cpp
>>>>> @@ -8,13 +8,16 @@
>>>>>
>>>>>   #include "libcamera/internal/dma_buf_allocator.h"
>>>>>
>>>>> +#include <algorithm>
>>>>>   #include <array>
>>>>>   #include <fcntl.h>
>>>>> +#include <optional>
>>>>>   #include <sys/ioctl.h>
>>>>>   #include <sys/mman.h>
>>>>>   #include <sys/stat.h>
>>>>>   #include <sys/types.h>
>>>>>   #include <unistd.h>
>>>>> +#include <vector>
>>>>>
>>>>>   #include <linux/dma-buf.h>
>>>>>   #include <linux/dma-heap.h>
>>>>> @@ -26,6 +29,8 @@
>>>>>
>>>>>   #include <libcamera/framebuffer.h>
>>>>>
>>>>> +#include "libcamera/internal/camera_manager.h"
>>>>> +
>>>>>   /**
>>>>>    * \file dma_buf_allocator.cpp
>>>>>    * \brief dma-buf allocator
>>>>> @@ -51,6 +56,26 @@ static constexpr std::array<DmaBufAllocatorInfo, 4> providerInfos = { {
>>>>>        { DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf, "/dev/udmabuf" },
>>>>>   } };
>>>>>
>>>>> +/* Built-in provider priority, used when no other order is specified. */
>>>>> +static constexpr std::array<DmaBufAllocator::DmaBufAllocatorFlag, 3>
>>>>> +     defaultProviderPriority = { {
>>>>> +             DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap,
>>>>> +             DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap,
>>>>> +             DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf,
>>>>> +     } };
>>>>> +
>>>>> +static std::optional<DmaBufAllocator::DmaBufAllocatorFlag>
>>>>> +providerFromName(const std::string &name)
>>>>> +{
>>>>> +     if (name == "cma")
>>>>> +             return DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap;
>>>>> +     if (name == "system")
>>>>> +             return DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap;
>>>>> +     if (name == "udmabuf")
>>>>> +             return DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf;
>>>>> +     return {};
>>>>> +}
>>>>> +
>>>>>   LOG_DEFINE_CATEGORY(DmaBufAllocator)
>>>>>
>>>>>   /**
>>>>> @@ -83,6 +108,7 @@ LOG_DEFINE_CATEGORY(DmaBufAllocator)
>>>>>
>>>>>   /**
>>>>>    * \brief Construct a DmaBufAllocator of a given type
>>>>> + * \param[in] cm The camera manager, used to access the global configuration
>>>>>    * \param[in] type The type(s) of the dma-buf providers to allocate from
>>>>>    *
>>>>>    * The dma-buf provider type is selected with the \a type parameter, which
>>>>> @@ -90,29 +116,67 @@ LOG_DEFINE_CATEGORY(DmaBufAllocator)
>>>>>    * the constructed DmaBufAllocator instance is invalid as indicated by
>>>>>    * the isValid() function.
>>>>>    *
>>>>> - * Multiple types can be selected by combining type flags, in which case
>>>>> - * the constructed DmaBufAllocator will match one of the types. If multiple
>>>>> - * requested types can work on the system, which provider is used is undefined.
>>>>> + * Multiple types can be selected by combining type flags. In that case the
>>>>> + * provider to use is chosen by priority: the providers listed in the
>>>>> + * `dma_buf_allocator.provider_priority` global configuration option (valid
>>>>> + * names are "cma", "system" and "udmabuf") take precedence, followed by
>>>>> + * libcamera's built-in order (CMA heap, system heap, then udmabuf). The first
>>>>> + * provider that is both requested and accessible is used.
>>>>>    */
>>>>> -DmaBufAllocator::DmaBufAllocator(DmaBufAllocatorFlags type)
>>>>> +DmaBufAllocator::DmaBufAllocator(const CameraManager &cm, DmaBufAllocatorFlags type)
>>>>>   {
>>>>> -     for (const auto &info : providerInfos) {
>>>>> -             if (!(type & info.type))
>>>>> -                     continue;
>>>>> +     std::vector<DmaBufAllocatorFlag> priority;
>>>>> +
>>>>> +     /*
>>>>> +      * The global configuration can override the provider priority;
>>>>> +      * providers not listed are appended in the built-in order, so no
>>>>> +      * acceptable provider is ever dropped.
>>>>> +      */
>>>>> +     const GlobalConfiguration &configuration = cm._d()->configuration();
>>>>> +     const std::vector<std::string> providerPriority =
>>>>> +             configuration.listOption({ "dma_buf_allocator", "provider_priority" })
>>>>> +                     .value_or(std::vector<std::string>{});
>>>>> +
>>>>> +     for (const std::string &name : providerPriority) {
>>>>> +             auto flag = providerFromName(name);
>>>>> +             if (flag)
>>>>> +                     priority.push_back(*flag);
>>>>> +             else
>>>>> +                     LOG(DmaBufAllocator, Warning)
>>>>> +                             << "Ignoring unknown dma-buf provider \""
>>>>> +                             << name << "\"";
>>>>> +     }
>>>>>
>>>>> -             int ret = ::open(info.deviceNodeName, O_RDONLY | O_CLOEXEC, 0);
>>>>> -             if (ret < 0) {
>>>>> -                     ret = errno;
>>>>> -                     LOG(DmaBufAllocator, Debug)
>>>>> -                             << "Failed to open " << info.deviceNodeName << ": "
>>>>> -                             << strerror(ret);
>>>>> +     for (DmaBufAllocatorFlag flag : defaultProviderPriority) {
>>>>> +             if (std::find(priority.begin(), priority.end(), flag) == priority.end())
>>>>> +                     priority.push_back(flag);
>>>>> +     }
>>>>> +
>>>>> +     for (DmaBufAllocatorFlag flag : priority) {
>>>>> +             if (!(type & flag))
>>>>>                        continue;
>>>>> +
>>>>> +             for (const auto &info : providerInfos) {
>>>>> +                     if (info.type != flag)
>>>>> +                             continue;
>>>>> +
>>>>> +                     int ret = ::open(info.deviceNodeName, O_RDONLY | O_CLOEXEC, 0);
>>>>> +                     if (ret < 0) {
>>>>> +                             ret = errno;
>>>>> +                             LOG(DmaBufAllocator, Debug)
>>>>> +                                     << "Failed to open " << info.deviceNodeName
>>>>> +                                     << ": " << strerror(ret);
>>>>> +                             continue;
>>>>> +                     }
>>>>> +
>>>>> +                     LOG(DmaBufAllocator, Debug) << "Using " << info.deviceNodeName;
>>>>> +                     providerHandle_ = UniqueFD(ret);
>>>>> +                     type_ = info.type;
>>>>> +                     break;
>>>>>                }
>>>>>
>>>>> -             LOG(DmaBufAllocator, Debug) << "Using " << info.deviceNodeName;
>>>>> -             providerHandle_ = UniqueFD(ret);
>>>>> -             type_ = info.type;
>>>>> -             break;
>>>>> +             if (providerHandle_.isValid())
>>>>> +                     break;
>>>>>        }
>>>>>
>>>>>        if (!providerHandle_.isValid())
>>>>> diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
>>>>> index 200e3b58724a9f307922aa9574cd92b5da637378..4ea0f89f700838c4f225fb1c1fac6d9e157fcfb5 100644
>>>>> --- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
>>>>> +++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
>>>>> @@ -39,7 +39,7 @@ class Vc4CameraData final : public RPi::CameraData
>>>>>   {
>>>>>   public:
>>>>>        Vc4CameraData(PipelineHandler *pipe)
>>>>> -             : RPi::CameraData(pipe)
>>>>> +             : RPi::CameraData(pipe), dmaHeap_(*pipe->cameraManager())
>>>>>        {
>>>>>        }
>>>>>
>>>>> diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp
>>>>> index e25dd85a7391b61bcea50e71e6264500d1e8a5b3..b8677e69a74d7fd8106022cc0c91ea8595917637 100644
>>>>> --- a/src/libcamera/pipeline/virtual/virtual.cpp
>>>>> +++ b/src/libcamera/pipeline/virtual/virtual.cpp
>>>>> @@ -238,9 +238,10 @@ bool PipelineHandlerVirtual::created_ = false;
>>>>>
>>>>>   PipelineHandlerVirtual::PipelineHandlerVirtual(CameraManager *manager)
>>>>>        : PipelineHandler(manager),
>>>>> -       dmaBufAllocator_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
>>>>> -                        DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
>>>>> -                        DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
>>>>> +       dmaBufAllocator_(*manager,
>>>>> +                        DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
>>>>> +                                DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
>>>>> +                                DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
>>>>>   {
>>>>>   }
>>>>>
>>>>> diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
>>>>> index f8efd24424a3981fd3eca03d2fcf27723480d08c..e146a0911d9db4a61e7fa54dd643d4e308458757 100644
>>>>> --- a/src/libcamera/software_isp/software_isp.cpp
>>>>> +++ b/src/libcamera/software_isp/software_isp.cpp
>>>>> @@ -24,6 +24,7 @@
>>>>>   #include <libcamera/stream.h>
>>>>>
>>>>>   #include "libcamera/internal/bayer_format.h"
>>>>> +#include "libcamera/internal/camera_manager.h"
>>>>>   #include "libcamera/internal/framebuffer.h"
>>>>>   #include "libcamera/internal/software_isp/debayer_params.h"
>>>>>
>>>>> @@ -81,9 +82,10 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)
>>>>>   SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
>>>>>                         ControlInfoMap *ipaControls)
>>>>>        : ispWorkerThread_("SWIspWorker"),
>>>>> -       dmaHeap_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
>>>>> -                DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
>>>>> -                DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
>>>>> +       dmaHeap_(*pipe->cameraManager(),
>>>>> +                DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
>>>>> +                        DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
>>>>> +                        DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
>>>>>   {
>>>>>        if (!dmaHeap_.isValid()) {
>>>>>                LOG(SoftwareIsp, Error) << "Failed to create DmaBufAllocator object";
>>>>>
>>>>> ---
>>>>> base-commit: 87c7285663aaad7608fdc18d5216ec6811c685c7
>>>>> change-id: 20260911-udma-a002f890212c
>>
>
Hans de Goede Oct. 3, 2026, 1:20 p.m. UTC | #6
Hi,

On 2-Oct-26 15:43, Barnabás Pőcze wrote:
> 2026. 09. 21. 13:21 keltezéssel, johannes.goede@oss.qualcomm.com írta:
>> Hi,
>>
>> On 21-Sep-26 9:50 AM, Laurent Pinchart wrote:
>>> Hi Naush,
>>>
>>> On Mon, Sep 21, 2026 at 08:45:21AM +0100, Naushir Patuck wrote:
>>>> On Sun, 20 Sept 2026 at 19:37, Laurent Pinchart wrote:
>>>>> On Thu, Sep 17, 2026 at 05:01:05PM +0800, Wenmeng Liu wrote:
>>>>>> DmaBufAllocator selects a provider using a fixed priority (CMA heap,
>>>>>> system heap, then udmabuf) and picks the first one that can be opened.
>>>>>> On platforms with a small CMA region this is suboptimal: CMA is always
>>>>>> chosen when present even though it may be too small, while udmabuf,
>>>>>> backed by pageable system memory, would work.
>>>>>>
>>>>>> Make the provider priority order configurable through the
>>>>>> 'dma_buf_allocator/provider_priority' global configuration option, an
>>>>>> ordered list of preferred provider names. Providers not listed are tried
>>>>>> afterwards in the built-in order (CMA heap, system heap, then udmabuf).
>>>>>> When the option is absent the historical order is kept, so behaviour is
>>>>>> unchanged by default.
>>>>>>
>>>>>> DmaBufAllocator reads this option itself. Its constructor now takes a
>>>>>> CameraManager reference to reach the GlobalConfiguration, following the
>>>>>> same pattern as the other software ISP helpers (SwStatsCpu, Benchmark,
>>>>>> Debayer). The callers only pass the camera manager and the providers they
>>>>>> accept, rather than parsing the configuration themselves. This lets a
>>>>>> platform prefer udmabuf by shipping a configuration.yaml.
>>>>>>
>>>>>> The set of providers stays limited to what the caller requested, so a
>>>>>> component that requires physically-contiguous memory (CMA only) is not
>>>>>> given a udmabuf-backed buffer.
>>>>>>
>>>>>> Document the new option in runtime_configuration.rst.
>>>>>
>>>>> I don't think this is a good solution. At best it's a dirty hack.
>>>>>
>>>>> I've long said we need a way to figure out in userspace how to allocate
>>>>> memory. I don't want to see more dirty hacks shoved in libcamera. This
>>>>> has to stop at some point, and I think the point is now.
>>>>>
>>>>> Buffer allocation has to move out of libcamera entirely. I don't want to
>>>>> merge this patch with a concrete and actionable plan to drop the
>>>>> FrameBufferAllocator completely and only import buffers in libcamera.
>>>>
>>>> I think this is a reasonable stance.  However, I assume pipeline
>>>> handlers would still be ok to allocate buffers through v4l2 for purely
>>>> internal use?
>>>
>>> Absolutely. And it will be up to each pipeline handler to decide whether
>>> those buffers should be allocated from V4L2, from a DMA heap, or through
>>> any other means. Sorry for not making that clear.
>>
>> Right, so this mostly is about allowing pipeline-handlers to not only
>> specify which sources/backends dma_buf_allocator should try, but also
>> in which order to try them.
>>
>> I suggested to Wenmeng in the downstream issue tracking this to make
>> this configurable through the config file, as an override to any
>> pipeline-handler provided order.
>>
>> This patch only provides the configfile override and is missing
>> the bits for the pipeline-handler(s) to specify the order.
>>
>> ATM users of the DmaBufAllocator class, specify which backends
>> are acceptable to use by passing a bitmask with 1 bit per
>> acceptable backend.
>>
>> How if we change the flags argument to the constructor:
>>
>> DmaBufAllocator(DmaBufAllocatorFlags flags);
>>
>> to a vector:
>>
>> DmaBufAllocator(const std::vector<DmaBufAllocatorType> types);
>>
>> and then have the constructor try the supported allocator types
>> in the order in which they are in the vector?
> 
>   std::span<const DmaBufAllocatorType> types
> 
> but otherwise I think that's a reasonable idea.
> 
> 
>>
>> This way pipeline-handlers can specify an order of preference
>> in which to try the different backends and everything else
>> stays the same.
> 
> So then you wouldn't even need the configuration file changes?
> If so, I think that's even better.

Yes pipeline handler specifies its preferred allocators
in the span and no configuration file setttings are necessary.

Regards,

Hans





>>>>>> Signed-off-by: Wenmeng Liu <wenmeng.liu@oss.qualcomm.com>
>>>>>> ---
>>>>>> Changes in v2:
>>>>>> - Move the configuration parsing from the callers into
>>>>>> DmaBufAllocator's constructor. The constructor now takes a
>>>>>> 'const CameraManager &cm' argument to reach the GlobalConfiguration
>>>>>> and reads 'dma_buf_allocator/provider_priority' itself, so the
>>>>>> callers no longer parse the config file. (Hans)
>>>>>> - Update the DmaBufAllocator callers (software ISP, virtual pipeline
>>>>>> and rpi/vc4) to the new constructor signature.
>>>>>> - Drop the "This is useful on platforms with a small CMA region..."
>>>>>> sentence from runtime_configuration.rst; the rationale is kept in
>>>>>> the commit message only. (Hans)
>>>>>> - Reword the commit message to describe reading the option inside
>>>>>> DmaBufAllocator rather than in each caller.
>>>>>> - Link to v1: https://lists.libcamera.org/pipermail/libcamera-devel/2026-September/062007.html
>>>>>> ---
>>>>>>   Documentation/runtime_configuration.rst        | 27 +++++++
>>>>>>   include/libcamera/internal/dma_buf_allocator.h |  4 +-
>>>>>>   src/libcamera/dma_buf_allocator.cpp            | 98 +++++++++++++++++++++-----
>>>>>>   src/libcamera/pipeline/rpi/vc4/vc4.cpp         |  2 +-
>>>>>>   src/libcamera/pipeline/virtual/virtual.cpp     |  7 +-
>>>>>>   src/libcamera/software_isp/software_isp.cpp    |  8 ++-
>>>>>>   6 files changed, 121 insertions(+), 25 deletions(-)
>>>>>>
>>>>>> diff --git a/Documentation/runtime_configuration.rst b/Documentation/runtime_configuration.rst
>>>>>> index a71b715a17245fc5fea405165323a607827a9ecb..79c6e4026adcdbb974f7398419b37f246717b9a9 100644
>>>>>> --- a/Documentation/runtime_configuration.rst
>>>>>> +++ b/Documentation/runtime_configuration.rst
>>>>>> @@ -33,6 +33,9 @@ file structure:
>>>>>>   ::
>>>>>>
>>>>>>     configuration:
>>>>>> +    dma_buf_allocator:
>>>>>> +      provider_priority:
>>>>>> +        - ... # dma-buf provider name: cma, system, or udmabuf
>>>>>>       ipa:
>>>>>>         force_isolation: # true/false
>>>>>>         config_paths:
>>>>>> @@ -62,6 +65,11 @@ Configuration file example
>>>>>>      ---
>>>>>>      version: 1
>>>>>>      configuration:
>>>>>> +     dma_buf_allocator:
>>>>>> +       provider_priority:
>>>>>> +         - udmabuf
>>>>>> +         - cma
>>>>>> +         - system
>>>>>>        ipa:
>>>>>>          config_paths:
>>>>>>            - /home/user/.libcamera/share/ipa
>>>>>> @@ -150,6 +158,25 @@ LIBCAMERA_SOFTISP_MODE, software_isp.mode
>>>>>>
>>>>>>      Example value: ``gpu``
>>>>>>
>>>>>> +dma_buf_allocator.provider_priority
>>>>>> +   Define an ordered list of dma-buf providers to try when libcamera
>>>>>> +   allocates buffers internally (for example for the software ISP). The
>>>>>> +   first requested provider that is available on the system is used. Valid
>>>>>> +   provider names are ``cma`` (CMA dma-heap), ``system`` (system dma-heap)
>>>>>> +   and ``udmabuf`` (memfd + /dev/udmabuf). Providers accepted by a component
>>>>>> +   but not listed here are tried after the listed ones, in libcamera's
>>>>>> +   built-in order.
>>>>>> +
>>>>>> +   Example value:
>>>>>> +
>>>>>> +   ::
>>>>>> +
>>>>>> +      dma_buf_allocator:
>>>>>> +        provider_priority:
>>>>>> +          - udmabuf
>>>>>> +          - cma
>>>>>> +          - system
>>>>>> +
>>>>>>   pipelines.simple.supported_devices.driver, pipelines.simple.supported_devices.software_isp
>>>>>>      Override whether software ISP is enabled for the given driver.
>>>>>>
>>>>>> diff --git a/include/libcamera/internal/dma_buf_allocator.h b/include/libcamera/internal/dma_buf_allocator.h
>>>>>> index 13600915a066d61c6f0dac2bfc7ec21d7fef33ef..07fd903170c92dc83b1b05d77aa261f6ac614c01 100644
>>>>>> --- a/include/libcamera/internal/dma_buf_allocator.h
>>>>>> +++ b/include/libcamera/internal/dma_buf_allocator.h
>>>>>> @@ -18,6 +18,7 @@
>>>>>>
>>>>>>   namespace libcamera {
>>>>>>
>>>>>> +class CameraManager;
>>>>>>   class FrameBuffer;
>>>>>>
>>>>>>   class DmaBufAllocator
>>>>>> @@ -31,7 +32,8 @@ public:
>>>>>>
>>>>>>        using DmaBufAllocatorFlags = Flags<DmaBufAllocatorFlag>;
>>>>>>
>>>>>> -     DmaBufAllocator(DmaBufAllocatorFlags flags = DmaBufAllocatorFlag::CmaHeap);
>>>>>> +     DmaBufAllocator(const CameraManager &cm,
>>>>>> +                     DmaBufAllocatorFlags type = DmaBufAllocatorFlag::CmaHeap);
>>>>>>        ~DmaBufAllocator();
>>>>>>        bool isValid() const { return providerHandle_.isValid(); }
>>>>>>        UniqueFD alloc(const char *name, std::size_t size);
>>>>>> diff --git a/src/libcamera/dma_buf_allocator.cpp b/src/libcamera/dma_buf_allocator.cpp
>>>>>> index c673d23ec761e62b746f8984dfbdd0b01a858a38..9d96f06014eaeed38f4dda19fc4a1a4586ea488b 100644
>>>>>> --- a/src/libcamera/dma_buf_allocator.cpp
>>>>>> +++ b/src/libcamera/dma_buf_allocator.cpp
>>>>>> @@ -8,13 +8,16 @@
>>>>>>
>>>>>>   #include "libcamera/internal/dma_buf_allocator.h"
>>>>>>
>>>>>> +#include <algorithm>
>>>>>>   #include <array>
>>>>>>   #include <fcntl.h>
>>>>>> +#include <optional>
>>>>>>   #include <sys/ioctl.h>
>>>>>>   #include <sys/mman.h>
>>>>>>   #include <sys/stat.h>
>>>>>>   #include <sys/types.h>
>>>>>>   #include <unistd.h>
>>>>>> +#include <vector>
>>>>>>
>>>>>>   #include <linux/dma-buf.h>
>>>>>>   #include <linux/dma-heap.h>
>>>>>> @@ -26,6 +29,8 @@
>>>>>>
>>>>>>   #include <libcamera/framebuffer.h>
>>>>>>
>>>>>> +#include "libcamera/internal/camera_manager.h"
>>>>>> +
>>>>>>   /**
>>>>>>    * \file dma_buf_allocator.cpp
>>>>>>    * \brief dma-buf allocator
>>>>>> @@ -51,6 +56,26 @@ static constexpr std::array<DmaBufAllocatorInfo, 4> providerInfos = { {
>>>>>>        { DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf, "/dev/udmabuf" },
>>>>>>   } };
>>>>>>
>>>>>> +/* Built-in provider priority, used when no other order is specified. */
>>>>>> +static constexpr std::array<DmaBufAllocator::DmaBufAllocatorFlag, 3>
>>>>>> +     defaultProviderPriority = { {
>>>>>> +             DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap,
>>>>>> +             DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap,
>>>>>> +             DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf,
>>>>>> +     } };
>>>>>> +
>>>>>> +static std::optional<DmaBufAllocator::DmaBufAllocatorFlag>
>>>>>> +providerFromName(const std::string &name)
>>>>>> +{
>>>>>> +     if (name == "cma")
>>>>>> +             return DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap;
>>>>>> +     if (name == "system")
>>>>>> +             return DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap;
>>>>>> +     if (name == "udmabuf")
>>>>>> +             return DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf;
>>>>>> +     return {};
>>>>>> +}
>>>>>> +
>>>>>>   LOG_DEFINE_CATEGORY(DmaBufAllocator)
>>>>>>
>>>>>>   /**
>>>>>> @@ -83,6 +108,7 @@ LOG_DEFINE_CATEGORY(DmaBufAllocator)
>>>>>>
>>>>>>   /**
>>>>>>    * \brief Construct a DmaBufAllocator of a given type
>>>>>> + * \param[in] cm The camera manager, used to access the global configuration
>>>>>>    * \param[in] type The type(s) of the dma-buf providers to allocate from
>>>>>>    *
>>>>>>    * The dma-buf provider type is selected with the \a type parameter, which
>>>>>> @@ -90,29 +116,67 @@ LOG_DEFINE_CATEGORY(DmaBufAllocator)
>>>>>>    * the constructed DmaBufAllocator instance is invalid as indicated by
>>>>>>    * the isValid() function.
>>>>>>    *
>>>>>> - * Multiple types can be selected by combining type flags, in which case
>>>>>> - * the constructed DmaBufAllocator will match one of the types. If multiple
>>>>>> - * requested types can work on the system, which provider is used is undefined.
>>>>>> + * Multiple types can be selected by combining type flags. In that case the
>>>>>> + * provider to use is chosen by priority: the providers listed in the
>>>>>> + * `dma_buf_allocator.provider_priority` global configuration option (valid
>>>>>> + * names are "cma", "system" and "udmabuf") take precedence, followed by
>>>>>> + * libcamera's built-in order (CMA heap, system heap, then udmabuf). The first
>>>>>> + * provider that is both requested and accessible is used.
>>>>>>    */
>>>>>> -DmaBufAllocator::DmaBufAllocator(DmaBufAllocatorFlags type)
>>>>>> +DmaBufAllocator::DmaBufAllocator(const CameraManager &cm, DmaBufAllocatorFlags type)
>>>>>>   {
>>>>>> -     for (const auto &info : providerInfos) {
>>>>>> -             if (!(type & info.type))
>>>>>> -                     continue;
>>>>>> +     std::vector<DmaBufAllocatorFlag> priority;
>>>>>> +
>>>>>> +     /*
>>>>>> +      * The global configuration can override the provider priority;
>>>>>> +      * providers not listed are appended in the built-in order, so no
>>>>>> +      * acceptable provider is ever dropped.
>>>>>> +      */
>>>>>> +     const GlobalConfiguration &configuration = cm._d()->configuration();
>>>>>> +     const std::vector<std::string> providerPriority =
>>>>>> +             configuration.listOption({ "dma_buf_allocator", "provider_priority" })
>>>>>> +                     .value_or(std::vector<std::string>{});
>>>>>> +
>>>>>> +     for (const std::string &name : providerPriority) {
>>>>>> +             auto flag = providerFromName(name);
>>>>>> +             if (flag)
>>>>>> +                     priority.push_back(*flag);
>>>>>> +             else
>>>>>> +                     LOG(DmaBufAllocator, Warning)
>>>>>> +                             << "Ignoring unknown dma-buf provider \""
>>>>>> +                             << name << "\"";
>>>>>> +     }
>>>>>>
>>>>>> -             int ret = ::open(info.deviceNodeName, O_RDONLY | O_CLOEXEC, 0);
>>>>>> -             if (ret < 0) {
>>>>>> -                     ret = errno;
>>>>>> -                     LOG(DmaBufAllocator, Debug)
>>>>>> -                             << "Failed to open " << info.deviceNodeName << ": "
>>>>>> -                             << strerror(ret);
>>>>>> +     for (DmaBufAllocatorFlag flag : defaultProviderPriority) {
>>>>>> +             if (std::find(priority.begin(), priority.end(), flag) == priority.end())
>>>>>> +                     priority.push_back(flag);
>>>>>> +     }
>>>>>> +
>>>>>> +     for (DmaBufAllocatorFlag flag : priority) {
>>>>>> +             if (!(type & flag))
>>>>>>                        continue;
>>>>>> +
>>>>>> +             for (const auto &info : providerInfos) {
>>>>>> +                     if (info.type != flag)
>>>>>> +                             continue;
>>>>>> +
>>>>>> +                     int ret = ::open(info.deviceNodeName, O_RDONLY | O_CLOEXEC, 0);
>>>>>> +                     if (ret < 0) {
>>>>>> +                             ret = errno;
>>>>>> +                             LOG(DmaBufAllocator, Debug)
>>>>>> +                                     << "Failed to open " << info.deviceNodeName
>>>>>> +                                     << ": " << strerror(ret);
>>>>>> +                             continue;
>>>>>> +                     }
>>>>>> +
>>>>>> +                     LOG(DmaBufAllocator, Debug) << "Using " << info.deviceNodeName;
>>>>>> +                     providerHandle_ = UniqueFD(ret);
>>>>>> +                     type_ = info.type;
>>>>>> +                     break;
>>>>>>                }
>>>>>>
>>>>>> -             LOG(DmaBufAllocator, Debug) << "Using " << info.deviceNodeName;
>>>>>> -             providerHandle_ = UniqueFD(ret);
>>>>>> -             type_ = info.type;
>>>>>> -             break;
>>>>>> +             if (providerHandle_.isValid())
>>>>>> +                     break;
>>>>>>        }
>>>>>>
>>>>>>        if (!providerHandle_.isValid())
>>>>>> diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
>>>>>> index 200e3b58724a9f307922aa9574cd92b5da637378..4ea0f89f700838c4f225fb1c1fac6d9e157fcfb5 100644
>>>>>> --- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
>>>>>> +++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
>>>>>> @@ -39,7 +39,7 @@ class Vc4CameraData final : public RPi::CameraData
>>>>>>   {
>>>>>>   public:
>>>>>>        Vc4CameraData(PipelineHandler *pipe)
>>>>>> -             : RPi::CameraData(pipe)
>>>>>> +             : RPi::CameraData(pipe), dmaHeap_(*pipe->cameraManager())
>>>>>>        {
>>>>>>        }
>>>>>>
>>>>>> diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp
>>>>>> index e25dd85a7391b61bcea50e71e6264500d1e8a5b3..b8677e69a74d7fd8106022cc0c91ea8595917637 100644
>>>>>> --- a/src/libcamera/pipeline/virtual/virtual.cpp
>>>>>> +++ b/src/libcamera/pipeline/virtual/virtual.cpp
>>>>>> @@ -238,9 +238,10 @@ bool PipelineHandlerVirtual::created_ = false;
>>>>>>
>>>>>>   PipelineHandlerVirtual::PipelineHandlerVirtual(CameraManager *manager)
>>>>>>        : PipelineHandler(manager),
>>>>>> -       dmaBufAllocator_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
>>>>>> -                        DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
>>>>>> -                        DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
>>>>>> +       dmaBufAllocator_(*manager,
>>>>>> +                        DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
>>>>>> +                                DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
>>>>>> +                                DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
>>>>>>   {
>>>>>>   }
>>>>>>
>>>>>> diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
>>>>>> index f8efd24424a3981fd3eca03d2fcf27723480d08c..e146a0911d9db4a61e7fa54dd643d4e308458757 100644
>>>>>> --- a/src/libcamera/software_isp/software_isp.cpp
>>>>>> +++ b/src/libcamera/software_isp/software_isp.cpp
>>>>>> @@ -24,6 +24,7 @@
>>>>>>   #include <libcamera/stream.h>
>>>>>>
>>>>>>   #include "libcamera/internal/bayer_format.h"
>>>>>> +#include "libcamera/internal/camera_manager.h"
>>>>>>   #include "libcamera/internal/framebuffer.h"
>>>>>>   #include "libcamera/internal/software_isp/debayer_params.h"
>>>>>>
>>>>>> @@ -81,9 +82,10 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)
>>>>>>   SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
>>>>>>                         ControlInfoMap *ipaControls)
>>>>>>        : ispWorkerThread_("SWIspWorker"),
>>>>>> -       dmaHeap_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
>>>>>> -                DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
>>>>>> -                DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
>>>>>> +       dmaHeap_(*pipe->cameraManager(),
>>>>>> +                DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
>>>>>> +                        DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
>>>>>> +                        DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
>>>>>>   {
>>>>>>        if (!dmaHeap_.isValid()) {
>>>>>>                LOG(SoftwareIsp, Error) << "Failed to create DmaBufAllocator object";
>>>>>>
>>>>>> ---
>>>>>> base-commit: 87c7285663aaad7608fdc18d5216ec6811c685c7
>>>>>> change-id: 20260911-udma-a002f890212c
>>>
>>
>

Patch
diff mbox series

diff --git a/Documentation/runtime_configuration.rst b/Documentation/runtime_configuration.rst
index a71b715a17245fc5fea405165323a607827a9ecb..79c6e4026adcdbb974f7398419b37f246717b9a9 100644
--- a/Documentation/runtime_configuration.rst
+++ b/Documentation/runtime_configuration.rst
@@ -33,6 +33,9 @@  file structure:
 ::
 
   configuration:
+    dma_buf_allocator:
+      provider_priority:
+        - ... # dma-buf provider name: cma, system, or udmabuf
     ipa:
       force_isolation: # true/false
       config_paths:
@@ -62,6 +65,11 @@  Configuration file example
    ---
    version: 1
    configuration:
+     dma_buf_allocator:
+       provider_priority:
+         - udmabuf
+         - cma
+         - system
      ipa:
        config_paths:
          - /home/user/.libcamera/share/ipa
@@ -150,6 +158,25 @@  LIBCAMERA_SOFTISP_MODE, software_isp.mode
 
    Example value: ``gpu``
 
+dma_buf_allocator.provider_priority
+   Define an ordered list of dma-buf providers to try when libcamera
+   allocates buffers internally (for example for the software ISP). The
+   first requested provider that is available on the system is used. Valid
+   provider names are ``cma`` (CMA dma-heap), ``system`` (system dma-heap)
+   and ``udmabuf`` (memfd + /dev/udmabuf). Providers accepted by a component
+   but not listed here are tried after the listed ones, in libcamera's
+   built-in order.
+
+   Example value:
+
+   ::
+
+      dma_buf_allocator:
+        provider_priority:
+          - udmabuf
+          - cma
+          - system
+
 pipelines.simple.supported_devices.driver, pipelines.simple.supported_devices.software_isp
    Override whether software ISP is enabled for the given driver.
 
diff --git a/include/libcamera/internal/dma_buf_allocator.h b/include/libcamera/internal/dma_buf_allocator.h
index 13600915a066d61c6f0dac2bfc7ec21d7fef33ef..07fd903170c92dc83b1b05d77aa261f6ac614c01 100644
--- a/include/libcamera/internal/dma_buf_allocator.h
+++ b/include/libcamera/internal/dma_buf_allocator.h
@@ -18,6 +18,7 @@ 
 
 namespace libcamera {
 
+class CameraManager;
 class FrameBuffer;
 
 class DmaBufAllocator
@@ -31,7 +32,8 @@  public:
 
 	using DmaBufAllocatorFlags = Flags<DmaBufAllocatorFlag>;
 
-	DmaBufAllocator(DmaBufAllocatorFlags flags = DmaBufAllocatorFlag::CmaHeap);
+	DmaBufAllocator(const CameraManager &cm,
+			DmaBufAllocatorFlags type = DmaBufAllocatorFlag::CmaHeap);
 	~DmaBufAllocator();
 	bool isValid() const { return providerHandle_.isValid(); }
 	UniqueFD alloc(const char *name, std::size_t size);
diff --git a/src/libcamera/dma_buf_allocator.cpp b/src/libcamera/dma_buf_allocator.cpp
index c673d23ec761e62b746f8984dfbdd0b01a858a38..9d96f06014eaeed38f4dda19fc4a1a4586ea488b 100644
--- a/src/libcamera/dma_buf_allocator.cpp
+++ b/src/libcamera/dma_buf_allocator.cpp
@@ -8,13 +8,16 @@ 
 
 #include "libcamera/internal/dma_buf_allocator.h"
 
+#include <algorithm>
 #include <array>
 #include <fcntl.h>
+#include <optional>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <vector>
 
 #include <linux/dma-buf.h>
 #include <linux/dma-heap.h>
@@ -26,6 +29,8 @@ 
 
 #include <libcamera/framebuffer.h>
 
+#include "libcamera/internal/camera_manager.h"
+
 /**
  * \file dma_buf_allocator.cpp
  * \brief dma-buf allocator
@@ -51,6 +56,26 @@  static constexpr std::array<DmaBufAllocatorInfo, 4> providerInfos = { {
 	{ DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf, "/dev/udmabuf" },
 } };
 
+/* Built-in provider priority, used when no other order is specified. */
+static constexpr std::array<DmaBufAllocator::DmaBufAllocatorFlag, 3>
+	defaultProviderPriority = { {
+		DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap,
+		DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap,
+		DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf,
+	} };
+
+static std::optional<DmaBufAllocator::DmaBufAllocatorFlag>
+providerFromName(const std::string &name)
+{
+	if (name == "cma")
+		return DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap;
+	if (name == "system")
+		return DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap;
+	if (name == "udmabuf")
+		return DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf;
+	return {};
+}
+
 LOG_DEFINE_CATEGORY(DmaBufAllocator)
 
 /**
@@ -83,6 +108,7 @@  LOG_DEFINE_CATEGORY(DmaBufAllocator)
 
 /**
  * \brief Construct a DmaBufAllocator of a given type
+ * \param[in] cm The camera manager, used to access the global configuration
  * \param[in] type The type(s) of the dma-buf providers to allocate from
  *
  * The dma-buf provider type is selected with the \a type parameter, which
@@ -90,29 +116,67 @@  LOG_DEFINE_CATEGORY(DmaBufAllocator)
  * the constructed DmaBufAllocator instance is invalid as indicated by
  * the isValid() function.
  *
- * Multiple types can be selected by combining type flags, in which case
- * the constructed DmaBufAllocator will match one of the types. If multiple
- * requested types can work on the system, which provider is used is undefined.
+ * Multiple types can be selected by combining type flags. In that case the
+ * provider to use is chosen by priority: the providers listed in the
+ * `dma_buf_allocator.provider_priority` global configuration option (valid
+ * names are "cma", "system" and "udmabuf") take precedence, followed by
+ * libcamera's built-in order (CMA heap, system heap, then udmabuf). The first
+ * provider that is both requested and accessible is used.
  */
-DmaBufAllocator::DmaBufAllocator(DmaBufAllocatorFlags type)
+DmaBufAllocator::DmaBufAllocator(const CameraManager &cm, DmaBufAllocatorFlags type)
 {
-	for (const auto &info : providerInfos) {
-		if (!(type & info.type))
-			continue;
+	std::vector<DmaBufAllocatorFlag> priority;
+
+	/*
+	 * The global configuration can override the provider priority;
+	 * providers not listed are appended in the built-in order, so no
+	 * acceptable provider is ever dropped.
+	 */
+	const GlobalConfiguration &configuration = cm._d()->configuration();
+	const std::vector<std::string> providerPriority =
+		configuration.listOption({ "dma_buf_allocator", "provider_priority" })
+			.value_or(std::vector<std::string>{});
+
+	for (const std::string &name : providerPriority) {
+		auto flag = providerFromName(name);
+		if (flag)
+			priority.push_back(*flag);
+		else
+			LOG(DmaBufAllocator, Warning)
+				<< "Ignoring unknown dma-buf provider \""
+				<< name << "\"";
+	}
 
-		int ret = ::open(info.deviceNodeName, O_RDONLY | O_CLOEXEC, 0);
-		if (ret < 0) {
-			ret = errno;
-			LOG(DmaBufAllocator, Debug)
-				<< "Failed to open " << info.deviceNodeName << ": "
-				<< strerror(ret);
+	for (DmaBufAllocatorFlag flag : defaultProviderPriority) {
+		if (std::find(priority.begin(), priority.end(), flag) == priority.end())
+			priority.push_back(flag);
+	}
+
+	for (DmaBufAllocatorFlag flag : priority) {
+		if (!(type & flag))
 			continue;
+
+		for (const auto &info : providerInfos) {
+			if (info.type != flag)
+				continue;
+
+			int ret = ::open(info.deviceNodeName, O_RDONLY | O_CLOEXEC, 0);
+			if (ret < 0) {
+				ret = errno;
+				LOG(DmaBufAllocator, Debug)
+					<< "Failed to open " << info.deviceNodeName
+					<< ": " << strerror(ret);
+				continue;
+			}
+
+			LOG(DmaBufAllocator, Debug) << "Using " << info.deviceNodeName;
+			providerHandle_ = UniqueFD(ret);
+			type_ = info.type;
+			break;
 		}
 
-		LOG(DmaBufAllocator, Debug) << "Using " << info.deviceNodeName;
-		providerHandle_ = UniqueFD(ret);
-		type_ = info.type;
-		break;
+		if (providerHandle_.isValid())
+			break;
 	}
 
 	if (!providerHandle_.isValid())
diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
index 200e3b58724a9f307922aa9574cd92b5da637378..4ea0f89f700838c4f225fb1c1fac6d9e157fcfb5 100644
--- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
+++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
@@ -39,7 +39,7 @@  class Vc4CameraData final : public RPi::CameraData
 {
 public:
 	Vc4CameraData(PipelineHandler *pipe)
-		: RPi::CameraData(pipe)
+		: RPi::CameraData(pipe), dmaHeap_(*pipe->cameraManager())
 	{
 	}
 
diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp
index e25dd85a7391b61bcea50e71e6264500d1e8a5b3..b8677e69a74d7fd8106022cc0c91ea8595917637 100644
--- a/src/libcamera/pipeline/virtual/virtual.cpp
+++ b/src/libcamera/pipeline/virtual/virtual.cpp
@@ -238,9 +238,10 @@  bool PipelineHandlerVirtual::created_ = false;
 
 PipelineHandlerVirtual::PipelineHandlerVirtual(CameraManager *manager)
 	: PipelineHandler(manager),
-	  dmaBufAllocator_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
-			   DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
-			   DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
+	  dmaBufAllocator_(*manager,
+			   DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
+				   DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
+				   DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
 {
 }
 
diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
index f8efd24424a3981fd3eca03d2fcf27723480d08c..e146a0911d9db4a61e7fa54dd643d4e308458757 100644
--- a/src/libcamera/software_isp/software_isp.cpp
+++ b/src/libcamera/software_isp/software_isp.cpp
@@ -24,6 +24,7 @@ 
 #include <libcamera/stream.h>
 
 #include "libcamera/internal/bayer_format.h"
+#include "libcamera/internal/camera_manager.h"
 #include "libcamera/internal/framebuffer.h"
 #include "libcamera/internal/software_isp/debayer_params.h"
 
@@ -81,9 +82,10 @@  LOG_DEFINE_CATEGORY(SoftwareIsp)
 SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
 			 ControlInfoMap *ipaControls)
 	: ispWorkerThread_("SWIspWorker"),
-	  dmaHeap_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
-		   DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
-		   DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
+	  dmaHeap_(*pipe->cameraManager(),
+		   DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
+			   DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
+			   DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
 {
 	if (!dmaHeap_.isValid()) {
 		LOG(SoftwareIsp, Error) << "Failed to create DmaBufAllocator object";