Hi,
Here's another attempt at supporting user-space allocations from a specific carved-out reserved memory region.
The initial problem we were discussing was that I'm currently working on a platform which has a memory layout with ECC enabled. However, enabling the ECC has a number of drawbacks on that platform: lower performance, increased memory usage, etc. So for things like framebuffers, the trade-off isn't great and thus there's a memory region with ECC disabled to allocate from for such use cases.
After a suggestion from John, I chose to first start using heap allocations flags to allow for userspace to ask for a particular ECC setup. This is then backed by a new heap type that runs from reserved memory chunks flagged as such, and the existing DT properties to specify the ECC properties.
After further discussion, it was considered that flags were not the right solution, and relying on the names of the heaps would be enough to let userspace know the kind of buffer it deals with.
Thus, even though the uAPI part of it had been dropped in this second version, we still needed a driver to create heaps out of carved-out memory regions. In addition to the original usecase, a similar driver can be found in BSPs from most vendors, so I believe it would be a useful addition to the kernel.
Some extra discussion with Rob Herring [1] came to the conclusion that some specific compatible for this is not great either, and as such an new driver probably isn't called for either.
Some other discussions we had with John [2] also dropped some hints that multiple CMA heaps might be a good idea, and some vendors seem to do that too.
So here's another attempt that doesn't affect the device tree at all and will just create a heap for every CMA reserved memory region.
It also falls nicely into the current plan we have to support cgroups in DRM/KMS and v4l2, which is an additional benefit.
Let me know what you think, Maxime
1: https://lore.kernel.org/all/20250707-cobalt-dingo-of-serenity-dbf92c@houat/ 2: https://lore.kernel.org/all/CANDhNCroe6ZBtN_o=c71kzFFaWK-fF5rCdnr9P5h1sgPOWS...
Let me know what you think, Maxime
Signed-off-by: Maxime Ripard mripard@kernel.org --- Changes in v7: - Invert the logic and register CMA heap from the reserved memory / dma contiguous code, instead of iterating over them from the CMA heap. - Link to v6: https://lore.kernel.org/r/20250709-dma-buf-ecc-heap-v6-0-dac9bf80f35d@kernel...
Changes in v6: - Drop the new driver and allocate a CMA heap for each region now - Dropped the binding - Rebased on 6.16-rc5 - Link to v5: https://lore.kernel.org/r/20250617-dma-buf-ecc-heap-v5-0-0abdc5863a4f@kernel...
Changes in v5: - Rebased on 6.16-rc2 - Switch from property to dedicated binding - Link to v4: https://lore.kernel.org/r/20250520-dma-buf-ecc-heap-v4-1-bd2e1f1bb42c@kernel...
Changes in v4: - Rebased on 6.15-rc7 - Map buffers only when map is actually called, not at allocation time - Deal with restricted-dma-pool and shared-dma-pool - Reword Kconfig options - Properly report dma_map_sgtable failures - Link to v3: https://lore.kernel.org/r/20250407-dma-buf-ecc-heap-v3-0-97cdd36a5f29@kernel...
Changes in v3: - Reworked global variable patch - Link to v2: https://lore.kernel.org/r/20250401-dma-buf-ecc-heap-v2-0-043fd006a1af@kernel...
Changes in v2: - Add vmap/vunmap operations - Drop ECC flags uapi - Rebase on top of 6.14 - Link to v1: https://lore.kernel.org/r/20240515-dma-buf-ecc-heap-v1-0-54cbbd049511@kernel...
--- Maxime Ripard (5): doc: dma-buf: List the heaps by name dma-buf: heaps: cma: Register list of CMA regions at boot dma: contiguous: Register reusable CMA regions at boot dma: contiguous: Reserve default CMA heap dma-buf: heaps: cma: Create CMA heap for each CMA reserved region
Documentation/userspace-api/dma-buf-heaps.rst | 24 ++++++++------ MAINTAINERS | 1 + drivers/dma-buf/heaps/Kconfig | 10 ------ drivers/dma-buf/heaps/cma_heap.c | 47 +++++++++++++++++---------- include/linux/dma-buf/heaps/cma.h | 16 +++++++++ kernel/dma/contiguous.c | 11 +++++++ 6 files changed, 72 insertions(+), 37 deletions(-) --- base-commit: 47633099a672fc7bfe604ef454e4f116e2c954b1 change-id: 20240515-dma-buf-ecc-heap-28a311d2c94e prerequisite-message-id: 20250610131231.1724627-1-jkangas@redhat.com prerequisite-patch-id: bc44be5968feb187f2bc1b8074af7209462b18e7 prerequisite-patch-id: f02a91b723e5ec01fbfedf3c3905218b43d432da prerequisite-patch-id: e944d0a3e22f2cdf4d3b3906e5603af934696deb
Best regards,
Since we're going to introduce multiple instances of the CMA heap driver, there's no single CMA heap anymore.
Let's use the heap name instead to differentiate between all the heaps available in the system.
While we're at it, let's also rework the backward compatibility part to make it easier to amend later on.
Signed-off-by: Maxime Ripard mripard@kernel.org --- Documentation/userspace-api/dma-buf-heaps.rst | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/Documentation/userspace-api/dma-buf-heaps.rst b/Documentation/userspace-api/dma-buf-heaps.rst index 1dfe5e7acd5a3c674323775176d81944147e40c0..17bf6829efd7963bc849765db54d327644e8c395 100644 --- a/Documentation/userspace-api/dma-buf-heaps.rst +++ b/Documentation/userspace-api/dma-buf-heaps.rst @@ -14,15 +14,16 @@ Heaps A heap represents a specific allocator. The Linux kernel currently supports the following heaps:
- The ``system`` heap allocates virtually contiguous, cacheable, buffers.
- - The ``cma`` heap allocates physically contiguous, cacheable, - buffers. Only present if a CMA region is present. Such a region is - usually created either through the kernel commandline through the - ``cma`` parameter, a memory region Device-Tree node with the - ``linux,cma-default`` property set, or through the ``CMA_SIZE_MBYTES`` or - ``CMA_SIZE_PERCENTAGE`` Kconfig options. The heap's name in devtmpfs is - ``default_cma_region``. For backwards compatibility, when the - ``DMABUF_HEAPS_CMA_LEGACY`` Kconfig option is set, a duplicate node is - created following legacy naming conventions; the legacy name might be - ``reserved``, ``linux,cma``, or ``default-pool``. + - The ``default_cma_region`` heap allocates physically contiguous, + cacheable, buffers. Only present if a CMA region is present. Such a + region is usually created either through the kernel commandline + through the ``cma`` parameter, a memory region Device-Tree node with + the ``linux,cma-default`` property set, or through the + ``CMA_SIZE_MBYTES`` or ``CMA_SIZE_PERCENTAGE`` Kconfig options. Prior + to Linux 6.17, its name wasn't stable and could be called + ``reserved``, ``linux,cma``, or ``default-pool``, depending on the + platform. From Linux 6.17 onwards, the creation of these heaps is + controlled through the ``DMABUF_HEAPS_CMA_LEGACY`` Kconfig option for + backwards compatibility.
In order to create a CMA heap instance for each CMA region found in the system, we need to register each of these instances.
While it would appear trivial, the CMA regions are created super early in the kernel boot process, before most of the subsystems are initialized. Thus, we can't just create an exported function to create a heap from the CMA region being initialized.
What we can do however is create a two-step process, where we collect all the CMA regions into an array early on, and then when we initialize the heaps we iterate over that array and create the heaps from the CMA regions we collected.
Signed-off-by: Maxime Ripard mripard@kernel.org --- MAINTAINERS | 1 + drivers/dma-buf/heaps/cma_heap.c | 14 ++++++++++++++ include/linux/dma-buf/heaps/cma.h | 16 ++++++++++++++++ 3 files changed, 31 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS index fad6cb025a1918beec113b576cf28b76151745ef..deb6930f28c82a7257f1d4610b9bc010de62a0c9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7095,10 +7095,11 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: Documentation/driver-api/dma-buf.rst F: Documentation/userspace-api/dma-buf-alloc-exchange.rst F: drivers/dma-buf/ F: include/linux/*fence.h F: include/linux/dma-buf.h +F: include/linux/dma-buf/ F: include/linux/dma-resv.h K: \bdma_(?:buf|fence|resv)\b
DMA GENERIC OFFLOAD ENGINE SUBSYSTEM M: Vinod Koul vkoul@kernel.org diff --git a/drivers/dma-buf/heaps/cma_heap.c b/drivers/dma-buf/heaps/cma_heap.c index 0df007111975447d555714d61ead9699287fd65a..2a901af635ed76cdb085915c03258c235e302792 100644 --- a/drivers/dma-buf/heaps/cma_heap.c +++ b/drivers/dma-buf/heaps/cma_heap.c @@ -12,10 +12,11 @@
#define pr_fmt(fmt) "cma_heap: " fmt
#include <linux/cma.h> #include <linux/dma-buf.h> +#include <linux/dma-buf/heaps/cma.h> #include <linux/dma-heap.h> #include <linux/dma-map-ops.h> #include <linux/err.h> #include <linux/highmem.h> #include <linux/io.h> @@ -25,10 +26,23 @@ #include <linux/slab.h> #include <linux/vmalloc.h>
#define DEFAULT_CMA_NAME "default_cma_region"
+static struct cma *dma_areas[MAX_CMA_AREAS] __initdata; +static unsigned int dma_areas_num __initdata; + +int __init dma_heap_cma_register_heap(struct cma *cma) +{ + if (dma_areas_num >= ARRAY_SIZE(dma_areas)) + return -EINVAL; + + dma_areas[dma_areas_num++] = cma; + + return 0; +} + struct cma_heap { struct dma_heap *heap; struct cma *cma; };
diff --git a/include/linux/dma-buf/heaps/cma.h b/include/linux/dma-buf/heaps/cma.h new file mode 100644 index 0000000000000000000000000000000000000000..e751479e21e703e24a5f799b4a7fc8bd0df3c1c4 --- /dev/null +++ b/include/linux/dma-buf/heaps/cma.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef DMA_BUF_HEAP_CMA_H_ +#define DMA_BUF_HEAP_CMA_H_ + +struct cma; + +#ifdef CONFIG_DMABUF_HEAPS_CMA +int dma_heap_cma_register_heap(struct cma *cma); +#else +static inline int dma_heap_cma_register_heap(struct cma *cma) +{ + return 0; +} +#endif // CONFIG_DMABUF_HEAPS_CMA + +#endif // DMA_BUF_HEAP_CMA_H_
In order to create a CMA dma-buf heap instance for each CMA heap region in the system, we need to collect all of them during boot.
They are created from two main sources: the reserved-memory regions in the device tree, and the default CMA region created from the configuration or command line parameters, if no default region is provided in the device tree.
Let's collect all the device-tree defined CMA regions flagged as reusable.
Signed-off-by: Maxime Ripard mripard@kernel.org --- kernel/dma/contiguous.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c index 8df0dfaaca18eeb0a20145512ba64425d2e7601e..e81982c0ee8f5c0654731adb611df4bcc4637c7c 100644 --- a/kernel/dma/contiguous.c +++ b/kernel/dma/contiguous.c @@ -40,10 +40,11 @@ #include <asm/page.h>
#include <linux/memblock.h> #include <linux/err.h> #include <linux/sizes.h> +#include <linux/dma-buf/heaps/cma.h> #include <linux/dma-map-ops.h> #include <linux/cma.h> #include <linux/nospec.h>
#ifdef CONFIG_CMA_SIZE_MBYTES @@ -490,9 +491,13 @@ static int __init rmem_cma_setup(struct reserved_mem *rmem) rmem->priv = cma;
pr_info("Reserved memory: created CMA memory pool at %pa, size %ld MiB\n", &rmem->base, (unsigned long)rmem->size / SZ_1M);
+ err = dma_heap_cma_register_heap(cma); + if (err) + pr_warn("Couldn't register CMA heap."); + return 0; } RESERVEDMEM_OF_DECLARE(cma, "shared-dma-pool", rmem_cma_setup); #endif
The CMA code, in addition to the reserved-memory regions in the device tree, will also register a default CMA region if the device tree doesn't provide any, with its size and position coming from either the kernel command-line or configuration.
Let's register that one for use to create a heap for it.
Signed-off-by: Maxime Ripard mripard@kernel.org --- kernel/dma/contiguous.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c index e81982c0ee8f5c0654731adb611df4bcc4637c7c..7aec2a559607b86e4d9df33ae2c004f7fb30fff1 100644 --- a/kernel/dma/contiguous.c +++ b/kernel/dma/contiguous.c @@ -237,17 +237,23 @@ void __init dma_contiguous_reserve(phys_addr_t limit) selected_size = max(size_bytes, cma_early_percent_memory()); #endif }
if (selected_size && !dma_contiguous_default_area) { + int ret; + pr_debug("%s: reserving %ld MiB for global area\n", __func__, (unsigned long)selected_size / SZ_1M);
dma_contiguous_reserve_area(selected_size, selected_base, selected_limit, &dma_contiguous_default_area, fixed); + + ret = dma_heap_cma_register_heap(dma_contiguous_default_area); + if (ret) + pr_warn("Couldn't register default CMA heap."); } }
void __weak dma_contiguous_early_fixup(phys_addr_t base, unsigned long size)
Aside from the main CMA region, it can be useful to allow userspace to allocate from the other CMA reserved regions.
Indeed, those regions can have specific properties that can be useful to a specific us-case.
For example, one of them platform I've been with has ECC enabled on the entire memory but for a specific region. Using that region to allocate framebuffers can be particular beneficial because enabling the ECC has a performance and memory footprint cost.
Thus, exposing these regions as heaps user-space can allocate from and import wherever needed allows to cover that use-case.
For now, only shared-dma-pools regions with the reusable property (ie, backed by CMA) are supported, but eventually we'll want to support other DMA pools types.
Since we collected all the CMA regions created during boot, we can simply iterate over all of them to create the heaps.
This has a weird interaction with the recent work on the CMA name, in particular the backward compatibility code created by commit 854acbe75ff4 ("dma-buf: heaps: Give default CMA heap a fixed name").
Indeed, the old name was either 'reserved', or the name of the reserved-memory region device tree node if the linux,cma-default property was set.
In both these cases, we have now collected this region during boot, and we're using the same name. So we're now largely redundant with the code to handle backward compatibility code, and we can thus remove it and the associated Kconfig option.
Signed-off-by: Maxime Ripard mripard@kernel.org --- Documentation/userspace-api/dma-buf-heaps.rst | 9 +++++--- drivers/dma-buf/heaps/Kconfig | 10 -------- drivers/dma-buf/heaps/cma_heap.c | 33 +++++++++++++-------------- 3 files changed, 22 insertions(+), 30 deletions(-)
diff --git a/Documentation/userspace-api/dma-buf-heaps.rst b/Documentation/userspace-api/dma-buf-heaps.rst index 17bf6829efd7963bc849765db54d327644e8c395..b78d2faeba62cda721a1f49d49e02bcb520ad429 100644 --- a/Documentation/userspace-api/dma-buf-heaps.rst +++ b/Documentation/userspace-api/dma-buf-heaps.rst @@ -22,8 +22,11 @@ following heaps: through the ``cma`` parameter, a memory region Device-Tree node with the ``linux,cma-default`` property set, or through the ``CMA_SIZE_MBYTES`` or ``CMA_SIZE_PERCENTAGE`` Kconfig options. Prior to Linux 6.17, its name wasn't stable and could be called ``reserved``, ``linux,cma``, or ``default-pool``, depending on the - platform. From Linux 6.17 onwards, the creation of these heaps is - controlled through the ``DMABUF_HEAPS_CMA_LEGACY`` Kconfig option for - backwards compatibility. + platform. + + - A heap will be created for each reusable region in the device tree + with the ``shared-dma-pool`` compatible, using the full device tree + node name as its name. The buffer semantics are identical to + ``default-cma-region``. diff --git a/drivers/dma-buf/heaps/Kconfig b/drivers/dma-buf/heaps/Kconfig index bb369b38b001af51721b56e065df92825022f1f1..a5eef06c422644e8aadaf5aff2bd9a33c49c1ba3 100644 --- a/drivers/dma-buf/heaps/Kconfig +++ b/drivers/dma-buf/heaps/Kconfig @@ -10,15 +10,5 @@ config DMABUF_HEAPS_CMA depends on DMABUF_HEAPS && DMA_CMA help Choose this option to enable dma-buf CMA heap. This heap is backed by the Contiguous Memory Allocator (CMA). If your system has these regions, you should say Y here. - -config DMABUF_HEAPS_CMA_LEGACY - bool "Legacy DMA-BUF CMA Heap" - default y - depends on DMABUF_HEAPS_CMA - help - Add a duplicate CMA-backed dma-buf heap with legacy naming derived - from the CMA area's devicetree node, or "reserved" if the area is not - defined in the devicetree. This uses the same underlying allocator as - CONFIG_DMABUF_HEAPS_CMA. diff --git a/drivers/dma-buf/heaps/cma_heap.c b/drivers/dma-buf/heaps/cma_heap.c index 2a901af635ed76cdb085915c03258c235e302792..42f88193eab9f8f4571064c7b3b8a73bca20fdf4 100644 --- a/drivers/dma-buf/heaps/cma_heap.c +++ b/drivers/dma-buf/heaps/cma_heap.c @@ -20,10 +20,12 @@ #include <linux/err.h> #include <linux/highmem.h> #include <linux/io.h> #include <linux/mm.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/of_reserved_mem.h> #include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/vmalloc.h>
#define DEFAULT_CMA_NAME "default_cma_region" @@ -407,35 +409,32 @@ static int __init __add_cma_heap(struct cma *cma, const char *name) }
return 0; }
-static int __init add_default_cma_heap(void) +static int __init add_cma_heaps(void) { struct cma *default_cma = dev_get_cma_area(NULL); - const char *legacy_cma_name; + unsigned int i; int ret;
- if (!default_cma) - return 0; + if (default_cma) { + ret = __add_cma_heap(default_cma, DEFAULT_CMA_NAME); + if (ret) + return ret; + }
- ret = __add_cma_heap(default_cma, DEFAULT_CMA_NAME); - if (ret) - return ret; + for (i = 0; i < dma_areas_num; i++) { + struct cma *cma = dma_areas[i];
- if (IS_ENABLED(CONFIG_DMABUF_HEAPS_CMA_LEGACY)) { - legacy_cma_name = cma_get_name(default_cma); - if (!strcmp(legacy_cma_name, DEFAULT_CMA_NAME)) { - pr_warn("legacy name and default name are the same, skipping legacy heap\n"); - return 0; + ret = __add_cma_heap(cma, cma_get_name(cma)); + if (ret) { + pr_warn("Failed to add CMA heap %s", cma_get_name(cma)); + continue; }
- ret = __add_cma_heap(default_cma, legacy_cma_name); - if (ret) - pr_warn("failed to add legacy heap: %pe\n", - ERR_PTR(ret)); }
return 0; } -module_init(add_default_cma_heap); +module_init(add_cma_heaps); MODULE_DESCRIPTION("DMA-BUF CMA Heap");
linaro-mm-sig@lists.linaro.org