* [PATCH v3 03/11] dt-bindings: gpu: host1x: Document memory-regions for NVDEC
From: Thierry Reding @ 2026-07-01 16:08 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
T.J. Mercier, Christian König, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon
Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
linaro-mm-sig, linux-trace-kernel, Thierry Reding
In-Reply-To: <20260701-tegra-vpr-v3-0-d80f7b871bb4@nvidia.com>
From: Thierry Reding <treding@nvidia.com>
The video protection region is a reserved memory region that can be used
for secure video playback. NVDEC can access this region to decode images
into securely.
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
.../devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Documentation/devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml b/Documentation/devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml
index 4eb325cfd296..bcaaabca945d 100644
--- a/Documentation/devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml
+++ b/Documentation/devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml
@@ -60,6 +60,14 @@ properties:
- const: dma-mem
- const: write
+ memory-region:
+ items:
+ - description: reference to the video protection memory region
+
+ memory-region-names:
+ items:
+ - const: protected
+
nvidia,memory-controller:
$ref: /schemas/types.yaml#/definitions/phandle
description:
--
2.54.0
^ permalink raw reply related
* [PATCH v3 04/11] arm64/mm: Add set_memory_device() and set_memory_normal()
From: Thierry Reding @ 2026-07-01 16:08 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
T.J. Mercier, Christian König, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon
Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
linaro-mm-sig, linux-trace-kernel, Thierry Reding, Chun Ng
In-Reply-To: <20260701-tegra-vpr-v3-0-d80f7b871bb4@nvidia.com>
From: Chun Ng <chunn@nvidia.com>
Add helpers to swap PROT_NORMAL and PROT_DEVICE_nGnRnE protection bits
on a kernel-linear-map range.
Signed-off-by: Chun Ng <chunn@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
arch/arm64/include/asm/set_memory.h | 2 ++
arch/arm64/mm/pageattr.c | 16 ++++++++++++++++
include/linux/set_memory.h | 11 +++++++++++
3 files changed, 29 insertions(+)
diff --git a/arch/arm64/include/asm/set_memory.h b/arch/arm64/include/asm/set_memory.h
index 90f61b17275e..6a7030609789 100644
--- a/arch/arm64/include/asm/set_memory.h
+++ b/arch/arm64/include/asm/set_memory.h
@@ -10,6 +10,8 @@ bool can_set_direct_map(void);
#define can_set_direct_map can_set_direct_map
int set_memory_valid(unsigned long addr, int numpages, int enable);
+int set_memory_device(unsigned long addr, int numpages);
+int set_memory_normal(unsigned long addr, int numpages);
int set_direct_map_invalid_noflush(struct page *page);
int set_direct_map_default_noflush(struct page *page);
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index bbe98ac9ad8c..871b59a6c9ea 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -251,6 +251,22 @@ int set_memory_valid(unsigned long addr, int numpages, int enable)
__pgprot(PTE_PRESENT_VALID_KERNEL));
}
+int set_memory_device(unsigned long addr, int numpages)
+{
+ return __change_memory_common(addr, PAGE_SIZE * numpages,
+ __pgprot(PROT_DEVICE_nGnRnE),
+ __pgprot(PROT_NORMAL));
+}
+EXPORT_SYMBOL_GPL(set_memory_device);
+
+int set_memory_normal(unsigned long addr, int numpages)
+{
+ return __change_memory_common(addr, PAGE_SIZE * numpages,
+ __pgprot(PROT_NORMAL),
+ __pgprot(PROT_DEVICE_nGnRnE));
+}
+EXPORT_SYMBOL_GPL(set_memory_normal);
+
int set_direct_map_invalid_noflush(struct page *page)
{
pgprot_t clear_mask = __pgprot(PTE_PRESENT_VALID_KERNEL);
diff --git a/include/linux/set_memory.h b/include/linux/set_memory.h
index 3030d9245f5a..abcb11378fdb 100644
--- a/include/linux/set_memory.h
+++ b/include/linux/set_memory.h
@@ -72,6 +72,17 @@ static inline int clear_mce_nospec(unsigned long pfn)
}
#endif
+#ifndef CONFIG_ARM64
+static inline int set_memory_device(unsigned long addr, int numpages)
+{
+ return 0;
+}
+static inline int set_memory_normal(unsigned long addr, int numpages)
+{
+ return 0;
+}
+#endif
+
#ifndef CONFIG_ARCH_HAS_MEM_ENCRYPT
static inline int set_memory_encrypted(unsigned long addr, int numpages)
{
--
2.54.0
^ permalink raw reply related
* [PATCH v3 05/11] bitmap: Add bitmap_allocate() function
From: Thierry Reding @ 2026-07-01 16:08 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
T.J. Mercier, Christian König, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon
Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
linaro-mm-sig, linux-trace-kernel, Thierry Reding
In-Reply-To: <20260701-tegra-vpr-v3-0-d80f7b871bb4@nvidia.com>
From: Thierry Reding <treding@nvidia.com>
This is similar to bitmap_allocate_region() but allows allocation of
non-power of two pages/bits.
While at it, reimplement bitmap_allocate_region() in terms of this new
helper to remove a sliver of code duplication.
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
include/linux/bitmap.h | 25 ++++++++++++++++++++-----
1 file changed, 20 insertions(+), 5 deletions(-)
diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index 8854acf77869..fb0aec4b17a1 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -721,10 +721,10 @@ void bitmap_release_region(unsigned long *bitmap, unsigned int pos, int order)
}
/**
- * bitmap_allocate_region - allocate bitmap region
+ * bitmap_allocate - allocate bitmap region
* @bitmap: array of unsigned longs corresponding to the bitmap
* @pos: beginning of bit region to allocate
- * @order: region size (log base 2 of number of bits) to allocate
+ * @len: number of bits to allocate
*
* Allocate (set bits in) a specified region of a bitmap.
*
@@ -732,16 +732,31 @@ void bitmap_release_region(unsigned long *bitmap, unsigned int pos, int order)
* free (not all bits were zero).
*/
static __always_inline
-int bitmap_allocate_region(unsigned long *bitmap, unsigned int pos, int order)
+int bitmap_allocate(unsigned long *bitmap, unsigned int pos, unsigned int len)
{
- unsigned int len = BIT(order);
-
if (find_next_bit(bitmap, pos + len, pos) < pos + len)
return -EBUSY;
bitmap_set(bitmap, pos, len);
return 0;
}
+/**
+ * bitmap_allocate_region - allocate bitmap region
+ * @bitmap: array of unsigned longs corresponding to the bitmap
+ * @pos: beginning of bit region to allocate
+ * @order: region size (log base 2 of number of bits) to allocate
+ *
+ * Allocate (set bits in) a specified region of a bitmap.
+ *
+ * Returns: 0 on success, or %-EBUSY if specified region wasn't
+ * free (not all bits were zero).
+ */
+static __always_inline
+int bitmap_allocate_region(unsigned long *bitmap, unsigned int pos, int order)
+{
+ return bitmap_allocate(bitmap, pos, BIT(order));
+}
+
/**
* bitmap_find_free_region - find a contiguous aligned mem region
* @bitmap: array of unsigned longs corresponding to the bitmap
--
2.54.0
^ permalink raw reply related
* [PATCH v3 06/11] mm/cma: Allow dynamically creating CMA areas
From: Thierry Reding @ 2026-07-01 16:08 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
T.J. Mercier, Christian König, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon
Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
linaro-mm-sig, linux-trace-kernel, Thierry Reding
In-Reply-To: <20260701-tegra-vpr-v3-0-d80f7b871bb4@nvidia.com>
From: Thierry Reding <treding@nvidia.com>
There is no technical reason why there should be a limited number of CMA
regions, so extract some code into helpers and use them to create extra
functions (cma_create() and cma_free()) that allow creating and freeing,
respectively, CMA regions dynamically at runtime.
The static array of CMA areas cannot be replaced by dynamically created
areas because for many of them, allocation must not fail and some cases
may need to initialize them before the slab allocator is even available.
To account for this, keep these "early" areas in a separate list and
track the dynamic areas in a separate list.
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v3:
- rebase on top of recent linux-next, update kernel/dma/contiguous.c
- use kzalloc_obj() instead of kzalloc() with sizeof()
Changes in v2:
- rename fixed number of CMA areas to reflect their main use
- account for pages in dynamically allocated regions
---
arch/arm/mm/dma-mapping.c | 2 +-
arch/s390/mm/init.c | 2 +-
include/linux/cma.h | 8 +-
kernel/dma/contiguous.c | 2 +-
mm/cma.c | 187 +++++++++++++++++++++++++++++++++++++---------
mm/cma.h | 5 +-
6 files changed, 165 insertions(+), 41 deletions(-)
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index f9bc53b60f99..934952ab2102 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -254,7 +254,7 @@ struct dma_contig_early_reserve {
unsigned long size;
};
-static struct dma_contig_early_reserve dma_mmu_remap[MAX_CMA_AREAS] __initdata;
+static struct dma_contig_early_reserve dma_mmu_remap[MAX_EARLY_CMA_AREAS] __initdata;
static int dma_mmu_remap_num __initdata;
diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c
index f07168a0d3dd..f8f78f1434ea 100644
--- a/arch/s390/mm/init.c
+++ b/arch/s390/mm/init.c
@@ -241,7 +241,7 @@ static int s390_cma_mem_notifier(struct notifier_block *nb,
mem_data.start = arg->start_pfn << PAGE_SHIFT;
mem_data.end = mem_data.start + (arg->nr_pages << PAGE_SHIFT);
if (action == MEM_GOING_OFFLINE)
- rc = cma_for_each_area(s390_cma_check_range, &mem_data);
+ rc = cma_for_each_early_area(s390_cma_check_range, &mem_data);
return notifier_from_errno(rc);
}
diff --git a/include/linux/cma.h b/include/linux/cma.h
index 8555d38a97b1..fb7a4923c3ba 100644
--- a/include/linux/cma.h
+++ b/include/linux/cma.h
@@ -7,7 +7,7 @@
#include <linux/numa.h>
#ifdef CONFIG_CMA_AREAS
-#define MAX_CMA_AREAS CONFIG_CMA_AREAS
+#define MAX_EARLY_CMA_AREAS CONFIG_CMA_AREAS
#endif
#define CMA_MAX_NAME 64
@@ -57,8 +57,14 @@ struct page *cma_alloc_frozen_compound(struct cma *cma, unsigned int order);
bool cma_release_frozen(struct cma *cma, const struct page *pages,
unsigned long count);
+extern int cma_for_each_early_area(int (*it)(struct cma *cma, void *data), void *data);
extern int cma_for_each_area(int (*it)(struct cma *cma, void *data), void *data);
extern bool cma_intersects(struct cma *cma, unsigned long start, unsigned long end);
extern void cma_reserve_pages_on_error(struct cma *cma);
+
+extern struct cma *cma_create(phys_addr_t base, phys_addr_t size,
+ unsigned int order_per_bit, const char *name);
+extern void cma_free(struct cma *cma);
+
#endif
diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c
index f754079a287d..7975551f69b3 100644
--- a/kernel/dma/contiguous.c
+++ b/kernel/dma/contiguous.c
@@ -52,7 +52,7 @@
#define CMA_SIZE_MBYTES 0
#endif
-static struct cma *dma_contiguous_areas[MAX_CMA_AREAS];
+static struct cma *dma_contiguous_areas[MAX_EARLY_CMA_AREAS];
static unsigned int dma_contiguous_areas_num;
static int dma_contiguous_insert_area(struct cma *cma)
diff --git a/mm/cma.c b/mm/cma.c
index a13ce4999b39..f989e2e98594 100644
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -34,7 +34,12 @@
#include "internal.h"
#include "cma.h"
-struct cma cma_areas[MAX_CMA_AREAS];
+static DEFINE_MUTEX(cma_lock);
+
+struct cma cma_early_areas[MAX_EARLY_CMA_AREAS];
+unsigned int cma_early_area_count;
+
+static LIST_HEAD(cma_areas);
unsigned int cma_area_count;
phys_addr_t cma_get_base(const struct cma *cma)
@@ -198,7 +203,6 @@ static void __init cma_activate_area(struct cma *cma)
free_reserved_page(pfn_to_page(pfn));
}
}
- totalcma_pages -= cma->count;
cma->available_count = cma->count = 0;
pr_err("CMA area %s could not be activated\n", cma->name);
}
@@ -207,8 +211,8 @@ static int __init cma_init_reserved_areas(void)
{
int i;
- for (i = 0; i < cma_area_count; i++)
- cma_activate_area(&cma_areas[i]);
+ for (i = 0; i < cma_early_area_count; i++)
+ cma_activate_area(&cma_early_areas[i]);
return 0;
}
@@ -219,41 +223,77 @@ void __init cma_reserve_pages_on_error(struct cma *cma)
set_bit(CMA_RESERVE_PAGES_ON_ERROR, &cma->flags);
}
+static void __init cma_init_area(struct cma *cma, const char *name,
+ phys_addr_t size, unsigned int order_per_bit)
+{
+ if (name)
+ strscpy(cma->name, name);
+ else
+ snprintf(cma->name, CMA_MAX_NAME, "cma%d\n", cma_area_count);
+
+ cma->available_count = cma->count = size >> PAGE_SHIFT;
+ cma->order_per_bit = order_per_bit;
+
+ INIT_LIST_HEAD(&cma->node);
+}
+
static int __init cma_new_area(const char *name, phys_addr_t size,
unsigned int order_per_bit,
struct cma **res_cma)
{
struct cma *cma;
- if (cma_area_count == ARRAY_SIZE(cma_areas)) {
+ if (cma_early_area_count == ARRAY_SIZE(cma_early_areas)) {
pr_err("Not enough slots for CMA reserved regions!\n");
return -ENOSPC;
}
+ mutex_lock(&cma_lock);
+
/*
* Each reserved area must be initialised later, when more kernel
* subsystems (like slab allocator) are available.
*/
- cma = &cma_areas[cma_area_count];
- cma_area_count++;
+ cma = &cma_early_areas[cma_early_area_count];
+ cma_early_area_count++;
- if (name)
- strscpy(cma->name, name);
- else
- snprintf(cma->name, CMA_MAX_NAME, "cma%d\n", cma_area_count);
+ cma_init_area(cma, name, size, order_per_bit);
- cma->available_count = cma->count = size >> PAGE_SHIFT;
- cma->order_per_bit = order_per_bit;
- *res_cma = cma;
totalcma_pages += cma->count;
+ *res_cma = cma;
+
+ mutex_unlock(&cma_lock);
return 0;
}
static void __init cma_drop_area(struct cma *cma)
{
+ mutex_lock(&cma_lock);
totalcma_pages -= cma->count;
- cma_area_count--;
+ cma_early_area_count--;
+ mutex_unlock(&cma_lock);
+}
+
+static int __init cma_check_memory(phys_addr_t base, phys_addr_t size)
+{
+ if (!size || !memblock_is_region_reserved(base, size))
+ return -EINVAL;
+
+ /*
+ * CMA uses CMA_MIN_ALIGNMENT_BYTES as alignment requirement which
+ * needs pageblock_order to be initialized. Let's enforce it.
+ */
+ if (!pageblock_order) {
+ pr_err("pageblock_order not yet initialized. Called during early boot?\n");
+ return -EINVAL;
+ }
+
+ /* ensure minimal alignment required by mm core */
+ if (!IS_ALIGNED(base | size, CMA_MIN_ALIGNMENT_BYTES))
+ return -EINVAL;
+
+ return 0;
}
/**
@@ -276,22 +316,9 @@ int __init cma_init_reserved_mem(phys_addr_t base, phys_addr_t size,
struct cma *cma;
int ret;
- /* Sanity checks */
- if (!size || !memblock_is_region_reserved(base, size))
- return -EINVAL;
-
- /*
- * CMA uses CMA_MIN_ALIGNMENT_BYTES as alignment requirement which
- * needs pageblock_order to be initialized. Let's enforce it.
- */
- if (!pageblock_order) {
- pr_err("pageblock_order not yet initialized. Called during early boot?\n");
- return -EINVAL;
- }
-
- /* ensure minimal alignment required by mm core */
- if (!IS_ALIGNED(base | size, CMA_MIN_ALIGNMENT_BYTES))
- return -EINVAL;
+ ret = cma_check_memory(base, size);
+ if (ret < 0)
+ return ret;
ret = cma_new_area(name, size, order_per_bit, &cma);
if (ret != 0)
@@ -444,7 +471,7 @@ static int __init __cma_declare_contiguous_nid(phys_addr_t *basep,
pr_debug("%s(size %pa, base %pa, limit %pa alignment %pa)\n",
__func__, &size, &base, &limit, &alignment);
- if (cma_area_count == ARRAY_SIZE(cma_areas)) {
+ if (cma_early_area_count == ARRAY_SIZE(cma_early_areas)) {
pr_err("Not enough slots for CMA reserved regions!\n");
return -ENOSPC;
}
@@ -1051,12 +1078,12 @@ bool cma_release_frozen(struct cma *cma, const struct page *pages,
return true;
}
-int cma_for_each_area(int (*it)(struct cma *cma, void *data), void *data)
+int cma_for_each_early_area(int (*it)(struct cma *cma, void *data), void *data)
{
int i;
- for (i = 0; i < cma_area_count; i++) {
- int ret = it(&cma_areas[i], data);
+ for (i = 0; i < cma_early_area_count; i++) {
+ int ret = it(&cma_early_areas[i], data);
if (ret)
return ret;
@@ -1065,6 +1092,25 @@ int cma_for_each_area(int (*it)(struct cma *cma, void *data), void *data)
return 0;
}
+int cma_for_each_area(int (*it)(struct cma *cma, void *data), void *data)
+{
+ struct cma *cma;
+
+ mutex_lock(&cma_lock);
+
+ list_for_each_entry(cma, &cma_areas, node) {
+ int ret = it(cma, data);
+
+ if (ret) {
+ mutex_unlock(&cma_lock);
+ return ret;
+ }
+ }
+
+ mutex_unlock(&cma_lock);
+ return 0;
+}
+
bool cma_intersects(struct cma *cma, unsigned long start, unsigned long end)
{
int r;
@@ -1147,3 +1193,74 @@ void __init *cma_reserve_early(struct cma *cma, unsigned long size)
return ret;
}
+
+struct cma *__init cma_create(phys_addr_t base, phys_addr_t size,
+ unsigned int order_per_bit, const char *name)
+{
+ struct cma *cma;
+ int ret;
+
+ ret = cma_check_memory(base, size);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ cma = kzalloc_obj(*cma, GFP_KERNEL);
+ if (!cma)
+ return ERR_PTR(-ENOMEM);
+
+ cma_init_area(cma, name, size, order_per_bit);
+ cma->ranges[0].base_pfn = PFN_DOWN(base);
+ cma->ranges[0].early_pfn = PFN_DOWN(base);
+ cma->ranges[0].count = cma->count;
+ cma->nranges = 1;
+
+ cma_activate_area(cma);
+
+ mutex_lock(&cma_lock);
+ list_add_tail(&cma->node, &cma_areas);
+ totalcma_pages += cma->count;
+ cma_area_count++;
+ mutex_unlock(&cma_lock);
+
+ return cma;
+}
+
+void cma_free(struct cma *cma)
+{
+ unsigned int i;
+
+ /*
+ * Safety check to prevent a CMA with active allocations from being
+ * released.
+ */
+ for (i = 0; i < cma->nranges; i++) {
+ unsigned long nbits = cma_bitmap_maxno(cma, &cma->ranges[i]);
+
+ if (!bitmap_empty(cma->ranges[i].bitmap, nbits)) {
+ WARN(1, "%s: range %u not empty\n", cma->name, i);
+ return;
+ }
+ }
+
+ /* free reserved pages and the bitmap */
+ for (i = 0; i < cma->nranges; i++) {
+ struct cma_memrange *cmr = &cma->ranges[i];
+ unsigned long end_pfn, pfn;
+
+ end_pfn = cmr->base_pfn + cmr->count;
+ for (pfn = cmr->base_pfn; pfn < end_pfn; pfn++)
+ free_reserved_page(pfn_to_page(pfn));
+
+ bitmap_free(cmr->bitmap);
+ }
+
+ mutex_destroy(&cma->alloc_mutex);
+
+ mutex_lock(&cma_lock);
+ totalcma_pages -= cma->count;
+ list_del(&cma->node);
+ cma_area_count--;
+ mutex_unlock(&cma_lock);
+
+ kfree(cma);
+}
diff --git a/mm/cma.h b/mm/cma.h
index c70180c36559..ae4db9819e38 100644
--- a/mm/cma.h
+++ b/mm/cma.h
@@ -41,6 +41,7 @@ struct cma {
unsigned long available_count;
unsigned int order_per_bit; /* Order of pages represented by one bit */
spinlock_t lock;
+ struct list_head node;
struct mutex alloc_mutex;
#ifdef CONFIG_CMA_DEBUGFS
struct hlist_head mem_head;
@@ -71,8 +72,8 @@ enum cma_flags {
CMA_ACTIVATED,
};
-extern struct cma cma_areas[MAX_CMA_AREAS];
-extern unsigned int cma_area_count;
+extern struct cma cma_early_areas[MAX_EARLY_CMA_AREAS];
+extern unsigned int cma_early_area_count;
static inline unsigned long cma_bitmap_maxno(struct cma *cma,
struct cma_memrange *cmr)
--
2.54.0
^ permalink raw reply related
* [PATCH v3 07/11] dma-buf: heaps: Add debugfs support
From: Thierry Reding @ 2026-07-01 16:08 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
T.J. Mercier, Christian König, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon
Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
linaro-mm-sig, linux-trace-kernel, Thierry Reding
In-Reply-To: <20260701-tegra-vpr-v3-0-d80f7b871bb4@nvidia.com>
From: Thierry Reding <treding@nvidia.com>
Add a callback to struct dma_heap_ops that heap providers can implement
to show information about the state of the heap in debugfs. A top-level
directory named "dma_heap" is created in debugfs and individual files
will be named after the heaps.
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
drivers/dma-buf/dma-heap.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++
include/linux/dma-heap.h | 2 ++
2 files changed, 58 insertions(+)
diff --git a/drivers/dma-buf/dma-heap.c b/drivers/dma-buf/dma-heap.c
index a76bf3f8b071..1ceb6ee8c05a 100644
--- a/drivers/dma-buf/dma-heap.c
+++ b/drivers/dma-buf/dma-heap.c
@@ -7,6 +7,7 @@
*/
#include <linux/cdev.h>
+#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/dma-buf.h>
#include <linux/dma-heap.h>
@@ -224,6 +225,46 @@ const char *dma_heap_get_name(struct dma_heap *heap)
}
EXPORT_SYMBOL_NS_GPL(dma_heap_get_name, "DMA_BUF_HEAP");
+#ifdef CONFIG_DEBUG_FS
+static int dma_heap_debug_show(struct seq_file *s, void *unused)
+{
+ struct dma_heap *heap = s->private;
+ int err = 0;
+
+ if (heap->ops && heap->ops->show)
+ err = heap->ops->show(s, heap);
+
+ return err;
+}
+DEFINE_SHOW_ATTRIBUTE(dma_heap_debug);
+
+static struct dentry *dma_heap_debugfs_dir;
+
+static void dma_heap_init_debugfs(void)
+{
+ struct dentry *dir;
+
+ dir = debugfs_create_dir("dma_heap", NULL);
+ if (IS_ERR(dir))
+ return;
+
+ dma_heap_debugfs_dir = dir;
+}
+
+static void dma_heap_exit_debugfs(void)
+{
+ debugfs_remove_recursive(dma_heap_debugfs_dir);
+}
+#else
+static void dma_heap_init_debugfs(void)
+{
+}
+
+static void dma_heap_exit_debugfs(void)
+{
+}
+#endif
+
/**
* dma_heap_add - adds a heap to dmabuf heaps
* @exp_info: information needed to register this heap
@@ -298,6 +339,13 @@ struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info)
/* Add heap to the list */
list_add(&heap->list, &heap_list);
+
+#ifdef CONFIG_DEBUG_FS
+ if (heap->ops && heap->ops->show)
+ debugfs_create_file(heap->name, 0444, dma_heap_debugfs_dir,
+ heap, &dma_heap_debug_fops);
+#endif
+
mutex_unlock(&heap_list_lock);
return heap;
@@ -334,6 +382,14 @@ static int dma_heap_init(void)
}
dma_heap_class->devnode = dma_heap_devnode;
+ dma_heap_init_debugfs();
+
return 0;
}
subsys_initcall(dma_heap_init);
+
+static void __exit dma_heap_exit(void)
+{
+ dma_heap_exit_debugfs();
+}
+__exitcall(dma_heap_exit);
diff --git a/include/linux/dma-heap.h b/include/linux/dma-heap.h
index 648328a64b27..1c9bed1f4dde 100644
--- a/include/linux/dma-heap.h
+++ b/include/linux/dma-heap.h
@@ -12,6 +12,7 @@
#include <linux/types.h>
struct dma_heap;
+struct seq_file;
/**
* struct dma_heap_ops - ops to operate on a given heap
@@ -24,6 +25,7 @@ struct dma_heap_ops {
unsigned long len,
u32 fd_flags,
u64 heap_flags);
+ int (*show)(struct seq_file *s, struct dma_heap *heap);
};
/**
--
2.54.0
^ permalink raw reply related
* [PATCH v3 08/11] dma-buf: heaps: Add support for Tegra VPR
From: Thierry Reding @ 2026-07-01 16:08 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
T.J. Mercier, Christian König, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon
Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
linaro-mm-sig, linux-trace-kernel, Thierry Reding
In-Reply-To: <20260701-tegra-vpr-v3-0-d80f7b871bb4@nvidia.com>
From: Thierry Reding <treding@nvidia.com>
NVIDIA Tegra SoCs commonly define a Video-Protection-Region, which is a
region of memory dedicated to content-protected video decode and
playback. This memory cannot be accessed by the CPU and only certain
hardware devices have access to it.
Expose the VPR as a DMA heap so that applications and drivers can
allocate buffers from this region for use-cases that require this kind
of protected memory.
VPR has a few very critical peculiarities. First, it must be a single
contiguous region of memory (there is a single pair of registers that
set the base address and size of the region), which is configured by
calling back into the secure monitor. The memory region also needs to
quite large for some use-cases because it needs to fit multiple video
frames (8K video should be supported), so VPR sizes of ~2 GiB are
expected. However, some devices cannot afford to reserve this amount
of memory for a particular use-case, and therefore the VPR must be
resizable.
Unfortunately, resizing the VPR is slightly tricky because the GPU found
on Tegra SoCs must be in reset during the VPR resize operation. This is
currently implemented by freezing all userspace processes and calling
invoking the GPU's freeze() implementation, resizing and the thawing the
GPU and userspace processes. This is quite heavy-handed, so eventually
it might be better to implement thawing/freezing in the GPU driver in
such a way that they block accesses to the GPU so that the VPR resize
operation can happen without suspending all userspace.
In order to balance the memory usage versus the amount of resizing that
needs to happen, the VPR is divided into multiple chunks. Each chunk is
implemented as a CMA area that is completely allocated on first use to
guarantee the contiguity of the VPR. Once all buffers from a chunk have
been freed, the CMA area is deallocated and the memory returned to the
system.
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v3:
- use set_memory_device() and set_memory_normal() helpers
- use kzalloc_obj() instead of kzalloc() with sizeof()
Changes in v2:
- cluster allocations to reduce the number of resize operations
- support cross-chunk allocation
---
drivers/dma-buf/heaps/Kconfig | 7 +
drivers/dma-buf/heaps/Makefile | 1 +
drivers/dma-buf/heaps/tegra-vpr.c | 1242 +++++++++++++++++++++++++++++++++++++
include/trace/events/tegra_vpr.h | 57 ++
4 files changed, 1307 insertions(+)
diff --git a/drivers/dma-buf/heaps/Kconfig b/drivers/dma-buf/heaps/Kconfig
index bb729e91545c..dd6035598d02 100644
--- a/drivers/dma-buf/heaps/Kconfig
+++ b/drivers/dma-buf/heaps/Kconfig
@@ -20,3 +20,10 @@ config DMABUF_HEAPS_CMA
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_TEGRA_VPR
+ bool "NVIDIA Tegra Video-Protected-Region DMA-BUF Heap"
+ depends on DMABUF_HEAPS && DMA_CMA
+ help
+ Choose this option to enable Video-Protected-Region (VPR) support on
+ a range of NVIDIA Tegra devices.
diff --git a/drivers/dma-buf/heaps/Makefile b/drivers/dma-buf/heaps/Makefile
index 974467791032..265b77a7b889 100644
--- a/drivers/dma-buf/heaps/Makefile
+++ b/drivers/dma-buf/heaps/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_DMABUF_HEAPS_SYSTEM) += system_heap.o
obj-$(CONFIG_DMABUF_HEAPS_CMA) += cma_heap.o
+obj-$(CONFIG_DMABUF_HEAPS_TEGRA_VPR) += tegra-vpr.o
diff --git a/drivers/dma-buf/heaps/tegra-vpr.c b/drivers/dma-buf/heaps/tegra-vpr.c
new file mode 100644
index 000000000000..918cf66ec222
--- /dev/null
+++ b/drivers/dma-buf/heaps/tegra-vpr.c
@@ -0,0 +1,1242 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DMA-BUF restricted heap exporter for NVIDIA Video-Protection-Region (VPR)
+ *
+ * Copyright (C) 2024-2026 NVIDIA Corporation
+ */
+
+#define pr_fmt(fmt) "tegra-vpr: " fmt
+
+#include <linux/arm-smccc.h>
+#include <linux/cma.h>
+#include <linux/debugfs.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-heap.h>
+#include <linux/find.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/set_memory.h>
+
+#include <linux/freezer.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/tegra_vpr.h>
+
+#define TEGRA_VPR_MAX_CHUNKS 64
+
+struct tegra_vpr;
+
+struct tegra_vpr_device {
+ struct list_head node;
+ struct device *dev;
+};
+
+struct tegra_vpr_chunk {
+ phys_addr_t start;
+ phys_addr_t limit;
+ size_t size;
+
+ struct tegra_vpr *vpr;
+ struct cma *cma;
+ bool active;
+
+ struct page *start_page;
+ unsigned int offset;
+ unsigned long virt;
+ pgoff_t num_pages;
+
+ unsigned int num_buffers;
+};
+
+struct tegra_vpr {
+ struct device_node *dev_node;
+ unsigned long align;
+ phys_addr_t base;
+ phys_addr_t size;
+ bool use_freezer;
+ bool resizable;
+
+ struct list_head buffers;
+ struct page *start_page;
+ unsigned long *bitmap;
+ pgoff_t num_pages;
+
+ /* resizable VPR */
+ DECLARE_BITMAP(active, TEGRA_VPR_MAX_CHUNKS);
+ struct tegra_vpr_chunk *chunks;
+ unsigned int num_chunks;
+
+ unsigned int first;
+ unsigned int last;
+
+ struct list_head devices;
+ struct mutex lock;
+};
+
+struct tegra_vpr_buffer {
+ struct list_head attachments;
+ struct tegra_vpr *vpr;
+ struct list_head list;
+ struct mutex lock;
+
+ struct page *start_page;
+ struct page **pages;
+ pgoff_t num_pages;
+ phys_addr_t start;
+ phys_addr_t limit;
+ size_t size;
+ int pageno;
+ int order;
+
+ DECLARE_BITMAP(chunks, TEGRA_VPR_MAX_CHUNKS);
+};
+
+struct tegra_vpr_attachment {
+ struct device *dev;
+ struct sg_table sgt;
+ struct list_head list;
+};
+
+#define ARM_SMCCC_TE_FUNC_PROGRAM_VPR 0x3
+
+#define ARM_SMCCC_VENDOR_SIP_TE_PROGRAM_VPR_FUNC_ID \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ ARM_SMCCC_OWNER_SIP, \
+ ARM_SMCCC_TE_FUNC_PROGRAM_VPR)
+
+static int tegra_vpr_set(phys_addr_t base, phys_addr_t size)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_smc(ARM_SMCCC_VENDOR_SIP_TE_PROGRAM_VPR_FUNC_ID, base, size,
+ 0, 0, 0, 0, 0, &res);
+
+ return res.a0;
+}
+
+static int tegra_vpr_get_extents(struct tegra_vpr *vpr, phys_addr_t *base,
+ phys_addr_t *size)
+{
+ phys_addr_t start = ~0, limit = 0;
+ unsigned int i;
+
+ for (i = 0; i < vpr->num_chunks; i++) {
+ struct tegra_vpr_chunk *chunk = &vpr->chunks[i];
+
+ if (chunk->active) {
+ if (chunk->start < start)
+ start = chunk->start;
+
+ if (chunk->limit > limit)
+ limit = chunk->limit;
+ }
+ }
+
+ if (limit > start) {
+ *size = limit - start;
+ *base = start;
+ } else {
+ *base = *size = 0;
+ }
+
+ return 0;
+}
+
+static int tegra_vpr_resize(struct tegra_vpr *vpr)
+{
+ struct tegra_vpr_device *node;
+ phys_addr_t base, size;
+ int err, status = 0;
+
+ err = tegra_vpr_get_extents(vpr, &base, &size);
+ if (err < 0) {
+ pr_err("%s(): failed to get VPR extents: %d\n", __func__, err);
+ return err;
+ }
+
+ if (vpr->use_freezer) {
+ err = freeze_processes();
+ if (err < 0) {
+ pr_err("%s(): failed to freeze processes: %d\n",
+ __func__, err);
+ return err;
+ }
+ }
+
+ list_for_each_entry(node, &vpr->devices, node) {
+ err = pm_generic_freeze(node->dev);
+ if (err < 0) {
+ pr_err("failed to runtime suspend %s: %d\n",
+ dev_name(node->dev), err);
+ goto thaw;
+ }
+ }
+
+ trace_tegra_vpr_set(base, size);
+
+ err = tegra_vpr_set(base, size);
+ if (err < 0) {
+ pr_err("failed to secure VPR: %d\n", err);
+ status = err;
+ }
+
+thaw:
+ list_for_each_entry_continue_reverse(node, &vpr->devices, node) {
+ err = pm_generic_thaw(node->dev);
+ if (err < 0) {
+ pr_err("failed to runtime resume %s\n",
+ dev_name(node->dev));
+ continue;
+ }
+ }
+
+ if (vpr->use_freezer)
+ thaw_processes();
+
+ return status;
+}
+
+static int __init tegra_vpr_chunk_init(struct tegra_vpr *vpr,
+ struct tegra_vpr_chunk *chunk,
+ phys_addr_t start, size_t size,
+ unsigned int order, const char *name)
+{
+ chunk->start = start;
+ chunk->limit = start + size;
+ chunk->size = size;
+ chunk->vpr = vpr;
+
+ chunk->cma = cma_create(start, size, order, name);
+ if (IS_ERR(chunk->cma)) {
+ pr_err("cma_create() failed: %ld\n", PTR_ERR(chunk->cma));
+ return PTR_ERR(chunk->cma);
+ }
+
+ chunk->offset = (start - vpr->base) >> PAGE_SHIFT;
+ chunk->num_pages = size >> PAGE_SHIFT;
+ chunk->num_buffers = 0;
+
+ /* CMA area is not reserved yet */
+ chunk->start_page = NULL;
+ chunk->virt = 0;
+
+ return 0;
+}
+
+static void tegra_vpr_chunk_free(struct tegra_vpr_chunk *chunk)
+{
+ cma_free(chunk->cma);
+}
+
+static inline bool tegra_vpr_chunk_is_last(const struct tegra_vpr_chunk *chunk)
+{
+ phys_addr_t limit = chunk->vpr->base + chunk->vpr->size;
+
+ return chunk->limit == limit;
+}
+
+static inline bool tegra_vpr_chunk_is_leaf(const struct tegra_vpr_chunk *chunk)
+{
+ const struct tegra_vpr_chunk *next = chunk + 1;
+
+ if (tegra_vpr_chunk_is_last(chunk))
+ return true;
+
+ return !next->active;
+}
+
+static int tegra_vpr_chunk_activate(struct tegra_vpr_chunk *chunk)
+{
+ unsigned long align = get_order(chunk->vpr->align);
+ int err;
+
+ trace_tegra_vpr_chunk_activate(chunk->start, chunk->limit);
+
+ chunk->start_page = cma_alloc(chunk->cma, chunk->num_pages, align,
+ false);
+ if (!chunk->start_page) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ chunk->virt = (unsigned long)page_to_virt(chunk->start_page);
+
+ err = set_memory_device(chunk->virt, chunk->num_pages);
+ if (err < 0)
+ goto free;
+
+ chunk->active = true;
+
+ return 0;
+
+free:
+ cma_release(chunk->cma, chunk->start_page, chunk->num_pages);
+fail:
+ chunk->start_page = NULL;
+ chunk->virt = 0;
+ return err;
+}
+
+static int tegra_vpr_chunk_deactivate(struct tegra_vpr_chunk *chunk)
+{
+ int err;
+
+ if (!chunk->active)
+ return 0;
+
+ /* do not deactivate if there are buffers left in this chunk */
+ if (WARN_ON(chunk->num_buffers > 0))
+ return -EBUSY;
+
+ trace_tegra_vpr_chunk_deactivate(chunk->start, chunk->limit);
+
+ err = set_memory_normal(chunk->virt, chunk->num_pages);
+ if (err < 0)
+ return err;
+
+ chunk->active = false;
+
+ cma_release(chunk->cma, chunk->start_page, chunk->num_pages);
+ chunk->start_page = NULL;
+ chunk->virt = 0;
+
+ return 0;
+}
+
+static bool tegra_vpr_chunk_overlaps(struct tegra_vpr_chunk *chunk,
+ unsigned int start, unsigned int limit)
+{
+ unsigned int first = chunk->offset;
+ unsigned int last = chunk->offset + chunk->num_pages - 1;
+
+ if (last < start || first >= limit)
+ return false;
+
+ return true;
+}
+
+static int tegra_vpr_activate_chunks(struct tegra_vpr *vpr,
+ struct tegra_vpr_buffer *buffer)
+{
+ DECLARE_BITMAP(dirty, vpr->num_chunks);
+ unsigned int i, bottom, top;
+ int err = 0, ret;
+
+ bitmap_zero(dirty, vpr->num_chunks);
+
+ /* activate any inactive chunks that overlap this buffer */
+ for_each_set_bit(i, buffer->chunks, vpr->num_chunks) {
+ struct tegra_vpr_chunk *chunk = &vpr->chunks[i];
+
+ if (chunk->active)
+ continue;
+
+ err = tegra_vpr_chunk_activate(chunk);
+ if (err < 0)
+ goto deactivate;
+
+ set_bit(i, vpr->active);
+ set_bit(i, dirty);
+ }
+
+ /*
+ * Activating chunks above may have created holes, but since the VPR
+ * can only ever be a single contiguous region, make sure to activate
+ * any missing chunks.
+ */
+ for_each_clear_bitrange(bottom, top, vpr->active, vpr->num_chunks) {
+ /* inactive chunks at the bottom or the top are harmless */
+ if (bottom == 0 || top == vpr->num_chunks)
+ continue;
+
+ for (i = bottom; i < top; i++) {
+ struct tegra_vpr_chunk *chunk = &vpr->chunks[i];
+
+ err = tegra_vpr_chunk_activate(chunk);
+ if (err < 0)
+ goto deactivate;
+
+ set_bit(i, vpr->active);
+ set_bit(i, dirty);
+ }
+ }
+
+ /* if any chunks have been activated, VPR needs to be resized */
+ if (!bitmap_empty(dirty, vpr->num_chunks)) {
+ err = tegra_vpr_resize(vpr);
+ if (err < 0) {
+ pr_err("failed to grow VPR: %d\n", err);
+ goto deactivate;
+ }
+ }
+
+ /* increment buffer count for each chunk */
+ for_each_set_bit(i, buffer->chunks, vpr->num_chunks)
+ vpr->chunks[i].num_buffers++;
+
+ return 0;
+
+deactivate:
+ /* deactivate any of the previously inactive chunks on failure */
+ for_each_set_bit(i, dirty, vpr->num_chunks) {
+ ret = tegra_vpr_chunk_deactivate(&vpr->chunks[i]);
+ if (ret < 0)
+ WARN(1, "failed to deactivate chunk #%u: %d\n", i, ret);
+
+ clear_bit(i, vpr->active);
+ }
+
+ return err;
+}
+
+/*
+ * Retrieve the range of pages within the activate region of the VPR.
+ */
+static bool tegra_vpr_get_active_range(struct tegra_vpr *vpr,
+ unsigned int *first,
+ unsigned int *last)
+{
+ unsigned long i, j;
+
+ i = find_first_bit(vpr->active, vpr->num_chunks);
+ if (i >= vpr->num_chunks)
+ return false;
+
+ j = find_last_bit(vpr->active, vpr->num_chunks);
+ if (j >= vpr->num_chunks)
+ return false;
+
+ *first = vpr->chunks[i].offset;
+ *last = vpr->chunks[j].offset + vpr->chunks[j].num_pages;
+
+ return true;
+}
+
+/*
+ * Try to find and allocate a free region within a specific page range.
+ * Returns the page number if successful, -ENOSPC otherwise.
+ *
+ * This function mimics bitmap_find_free_region() but restricts the search
+ * to a specific range to enable allocation within individual chunks.
+ */
+static int tegra_vpr_find_free_region_in_range(struct tegra_vpr *vpr,
+ unsigned int start_page,
+ unsigned int end_page,
+ unsigned int num_pages,
+ unsigned int align)
+{
+ unsigned int pos, next = ALIGN(start_page, align);
+
+ /* Scan through aligned positions, trying to allocate at each one */
+ for (pos = next; pos + num_pages <= end_page; pos = next) {
+ next = find_next_bit(vpr->bitmap, pos + num_pages, pos);
+
+ if (next >= pos + num_pages) {
+ bitmap_set(vpr->bitmap, pos, num_pages);
+ return pos;
+ }
+
+ next = find_next_zero_bit(vpr->bitmap, vpr->num_pages, next);
+ next = ALIGN(next, align);
+ }
+
+ return -ENOSPC;
+}
+
+static int tegra_vpr_find_free_region(struct tegra_vpr *vpr,
+ unsigned int num_pages,
+ unsigned long align)
+{
+ return tegra_vpr_find_free_region_in_range(vpr, 0, vpr->num_pages - 1,
+ num_pages, align);
+}
+
+static int tegra_vpr_find_free_region_clustered(struct tegra_vpr *vpr,
+ unsigned int num_pages,
+ unsigned int align)
+{
+ unsigned int target, first, last;
+ int pageno;
+
+ /*
+ * If there are no allocations, abort the clustered allocation scheme
+ * and use the generic allocation scheme instead.
+ */
+ if (vpr->first > vpr->last)
+ return -ENOSPC;
+
+ /*
+ * First, try to allocate within the currently allocated region. This
+ * keeps allocations tightly packed and minimizes the VPR size needed.
+ */
+ pageno = tegra_vpr_find_free_region_in_range(vpr, vpr->first,
+ vpr->last + 1, num_pages,
+ align);
+ if (pageno >= 0)
+ return pageno;
+
+ /*
+ * If not enough free space exists within the currently allocated
+ * region, check to see if the allocation fits anywhere within the
+ * active region, avoiding the need to resize the VPR.
+ */
+ if (tegra_vpr_get_active_range(vpr, &first, &last)) {
+ pageno = tegra_vpr_find_free_region_in_range(vpr, first, last,
+ num_pages, align);
+ if (pageno >= 0)
+ return pageno;
+ }
+
+ /*
+ * If not enough free space exists within the currently active region,
+ * try to allocate adjacent to it to grow it contiguously and ensure
+ * optimal packing.
+ */
+
+ /*
+ * Calculate where the allocation should start to end right at the
+ * first allocated page, with proper alignment.
+ */
+ if (vpr->first >= num_pages) {
+ target = ALIGN_DOWN(vpr->first - num_pages, align);
+
+ if (!bitmap_allocate(vpr->bitmap, target, num_pages))
+ return target;
+ }
+
+ /* Try after the last allocation */
+ target = ALIGN(vpr->last + 1, align);
+
+ if (target + num_pages <= vpr->num_pages &&
+ !bitmap_allocate(vpr->bitmap, target, num_pages))
+ return target;
+
+ /*
+ * Couldn't allocate at the ideal adjacent position, search for any
+ * available space before the first allocated page.
+ */
+ pageno = tegra_vpr_find_free_region_in_range(vpr, 0, vpr->first,
+ num_pages, align);
+ if (pageno >= 0)
+ return pageno;
+
+ /*
+ * Couldn't allocate at the ideal adjacent position, search
+ * for any available space after the last allocated page.
+ */
+ pageno = tegra_vpr_find_free_region_in_range(vpr, vpr->last + 1,
+ vpr->num_pages, num_pages,
+ align);
+ if (pageno >= 0)
+ return pageno;
+
+ return -ENOSPC;
+}
+
+/*
+ * Find a free region, preferring locations near existing allocations to
+ * minimize VPR fragmentation. The allocation strategy is to first allocate
+ * within or adjacent to the existing region to keep allocations clustered.
+ * Otherwise fall back to a generic allocation using the first available
+ * space.
+ *
+ * This approach focuses on page-level allocation first, then the chunk
+ * system determines which chunks need to be activated based on where the
+ * pages ended up.
+ */
+static int tegra_vpr_allocate_region(struct tegra_vpr *vpr,
+ unsigned int num_pages,
+ unsigned int align)
+{
+ int pageno;
+
+ /*
+ * For non-resizable VPR (no chunks), use simple first-fit allocation.
+ * Clustering optimization is only beneficial for resizable VPR where
+ * keeping allocations together minimizes the active VPR size.
+ */
+ if (vpr->num_chunks == 0)
+ return tegra_vpr_find_free_region(vpr, num_pages, align);
+
+ /*
+ * Check if there are any existing allocations in the bitmap. If so,
+ * try to allocate near them to minimize fragmentation.
+ */
+ pageno = tegra_vpr_find_free_region_clustered(vpr, num_pages, align);
+ if (pageno >= 0)
+ return pageno;
+
+ /*
+ * If there are no existing allocations, or no space adjacent to them,
+ * fall back to the first available space anywhere in the VPR.
+ */
+ pageno = tegra_vpr_find_free_region(vpr, num_pages, align);
+ if (pageno >= 0)
+ return pageno;
+
+ return -ENOSPC;
+}
+
+static struct tegra_vpr_buffer *
+tegra_vpr_buffer_allocate(struct tegra_vpr *vpr, size_t size)
+{
+ unsigned int num_pages = size >> PAGE_SHIFT;
+ unsigned int order = get_order(size);
+ struct tegra_vpr_buffer *buffer;
+ unsigned long first, last;
+ int pageno, err;
+ pgoff_t i;
+
+ /*
+ * "order" defines the alignment and size, so this may result in
+ * fragmented memory depending on the allocation patterns. However,
+ * since this is used primarily for video frames, it is expected that
+ * a number of buffers of the same size will be allocated, so
+ * fragmentation should be negligible.
+ */
+ pageno = tegra_vpr_allocate_region(vpr, num_pages, 1);
+ if (pageno < 0)
+ return ERR_PTR(pageno);
+
+ first = find_first_bit(vpr->bitmap, vpr->num_pages);
+ last = find_last_bit(vpr->bitmap, vpr->num_pages);
+
+ buffer = kzalloc_obj(*buffer, GFP_KERNEL);
+ if (!buffer) {
+ err = -ENOMEM;
+ goto release;
+ }
+
+ INIT_LIST_HEAD(&buffer->attachments);
+ INIT_LIST_HEAD(&buffer->list);
+ mutex_init(&buffer->lock);
+ buffer->start = vpr->base + (pageno << PAGE_SHIFT);
+ buffer->limit = buffer->start + size;
+ buffer->size = size;
+ buffer->num_pages = num_pages;
+ buffer->pageno = pageno;
+ buffer->order = order;
+
+ buffer->pages = kmalloc_array(buffer->num_pages,
+ sizeof(*buffer->pages),
+ GFP_KERNEL);
+ if (!buffer->pages) {
+ err = -ENOMEM;
+ goto free;
+ }
+
+ /* track which chunks this buffer overlaps */
+ if (vpr->num_chunks > 0) {
+ unsigned int limit = buffer->pageno + buffer->num_pages, i;
+
+ for (i = 0; i < vpr->num_chunks; i++) {
+ struct tegra_vpr_chunk *chunk = &vpr->chunks[i];
+
+ if (tegra_vpr_chunk_overlaps(chunk, pageno, limit))
+ set_bit(i, buffer->chunks);
+ }
+
+ /* activate chunks if necessary */
+ err = tegra_vpr_activate_chunks(vpr, buffer);
+ if (err < 0)
+ goto free;
+
+ /* track first and last allocated pages */
+ if (buffer->pageno < vpr->first)
+ vpr->first = buffer->pageno;
+
+ if (limit - 1 > vpr->last)
+ vpr->last = limit - 1;
+ }
+
+ for (i = 0; i < buffer->num_pages; i++)
+ buffer->pages[i] = &vpr->start_page[pageno + i];
+
+ return buffer;
+
+free:
+ kfree(buffer->pages);
+ kfree(buffer);
+release:
+ bitmap_release_region(vpr->bitmap, pageno, order);
+ return ERR_PTR(err);
+}
+
+static void tegra_vpr_buffer_release(struct tegra_vpr_buffer *buffer)
+{
+ struct tegra_vpr *vpr = buffer->vpr;
+ struct tegra_vpr_buffer *entry;
+ unsigned long first, last;
+ unsigned int i;
+
+ /*
+ * Decrement buffer count for each overlapping chunk. Note that chunks
+ * are not deactivated here yet, that's done in tegra_vpr_recycle()
+ * instead.
+ */
+ for_each_set_bit(i, buffer->chunks, vpr->num_chunks) {
+ if (!WARN_ON(vpr->chunks[i].num_buffers == 0))
+ vpr->chunks[i].num_buffers--;
+ }
+
+ /* track first and last allocated pages */
+ if (list_is_first(&buffer->list, &vpr->buffers) &&
+ list_is_last(&buffer->list, &vpr->buffers)) {
+ /* if there are no remaining buffers after this, reset */
+ vpr->first = ~0U;
+ vpr->last = 0U;
+ } else if (list_is_first(&buffer->list, &vpr->buffers)) {
+ entry = list_next_entry(buffer, list);
+ vpr->first = entry->pageno;
+ } else if (list_is_last(&buffer->list, &vpr->buffers)) {
+ entry = list_prev_entry(buffer, list);
+ vpr->last = entry->pageno + entry->num_pages - 1;
+ }
+
+ bitmap_release_region(vpr->bitmap, buffer->pageno, buffer->order);
+ list_del(&buffer->list);
+ kfree(buffer->pages);
+ kfree(buffer);
+
+ first = find_first_bit(vpr->bitmap, vpr->num_pages);
+ last = find_last_bit(vpr->bitmap, vpr->num_pages);
+}
+
+static int tegra_vpr_attach(struct dma_buf *buf,
+ struct dma_buf_attachment *attachment)
+{
+ struct tegra_vpr_buffer *buffer = buf->priv;
+ struct tegra_vpr_attachment *attach;
+ int err;
+
+ attach = kzalloc_obj(*attach, GFP_KERNEL);
+ if (!attach)
+ return -ENOMEM;
+
+ err = sg_alloc_table_from_pages(&attach->sgt, buffer->pages,
+ buffer->num_pages, 0, buffer->size,
+ GFP_KERNEL);
+ if (err < 0)
+ goto free;
+
+ attach->dev = attach->dev;
+ INIT_LIST_HEAD(&attach->list);
+ attachment->priv = attach;
+
+ mutex_lock(&buffer->lock);
+ list_add(&attach->list, &buffer->attachments);
+ mutex_unlock(&buffer->lock);
+
+ return 0;
+
+free:
+ kfree(attach);
+ return err;
+}
+
+static void tegra_vpr_detach(struct dma_buf *buf,
+ struct dma_buf_attachment *attachment)
+{
+ struct tegra_vpr_buffer *buffer = buf->priv;
+ struct tegra_vpr_attachment *attach = attachment->priv;
+
+ mutex_lock(&buffer->lock);
+ list_del(&attach->list);
+ mutex_unlock(&buffer->lock);
+
+ sg_free_table(&attach->sgt);
+ kfree(attach);
+}
+
+static struct sg_table *
+tegra_vpr_map_dma_buf(struct dma_buf_attachment *attachment,
+ enum dma_data_direction direction)
+{
+ struct tegra_vpr_attachment *attach = attachment->priv;
+ struct sg_table *sgt = &attach->sgt;
+ int err;
+
+ err = dma_map_sgtable(attachment->dev, sgt, direction,
+ DMA_ATTR_SKIP_CPU_SYNC);
+ if (err < 0)
+ return ERR_PTR(err);
+
+ return sgt;
+}
+
+static void tegra_vpr_unmap_dma_buf(struct dma_buf_attachment *attachment,
+ struct sg_table *sgt,
+ enum dma_data_direction direction)
+{
+ dma_unmap_sgtable(attachment->dev, sgt, direction,
+ DMA_ATTR_SKIP_CPU_SYNC);
+}
+
+static void tegra_vpr_recycle(struct tegra_vpr *vpr)
+{
+ DECLARE_BITMAP(dirty, vpr->num_chunks);
+ unsigned int i;
+ int err;
+
+ bitmap_zero(dirty, vpr->num_chunks);
+
+ /*
+ * Deactivate any unused chunks from the bottom...
+ */
+ for (i = 0; i < vpr->num_chunks; i++) {
+ struct tegra_vpr_chunk *chunk = &vpr->chunks[i];
+
+ if (!chunk->active)
+ continue;
+
+ if (chunk->num_buffers > 0)
+ break;
+
+ err = tegra_vpr_chunk_deactivate(chunk);
+ if (err < 0)
+ pr_err("failed to deactivate chunk #%u\n", i);
+ else {
+ clear_bit(i, vpr->active);
+ set_bit(i, dirty);
+ }
+ }
+
+ /*
+ * ... and the top.
+ */
+ for (i = 0; i < vpr->num_chunks; i++) {
+ unsigned int index = vpr->num_chunks - i - 1;
+ struct tegra_vpr_chunk *chunk = &vpr->chunks[index];
+
+ if (!chunk->active)
+ continue;
+
+ if (chunk->num_buffers > 0)
+ break;
+
+ err = tegra_vpr_chunk_deactivate(chunk);
+ if (err < 0)
+ pr_err("failed to deactivate chunk #%u\n", index);
+ else {
+ clear_bit(i, vpr->active);
+ set_bit(i, dirty);
+ }
+ }
+
+ if (!bitmap_empty(dirty, vpr->num_chunks)) {
+ err = tegra_vpr_resize(vpr);
+ if (err < 0) {
+ pr_err("failed to shrink VPR: %d\n", err);
+ goto activate;
+ }
+ }
+
+ return;
+
+activate:
+ for_each_set_bit(i, dirty, vpr->num_chunks) {
+ err = tegra_vpr_chunk_activate(&vpr->chunks[i]);
+ if (WARN_ON(err < 0))
+ pr_err("failed to activate chunk #%u: %d\n", i, err);
+ }
+}
+
+static void tegra_vpr_release(struct dma_buf *buf)
+{
+ struct tegra_vpr_buffer *buffer = buf->priv;
+ struct tegra_vpr *vpr = buffer->vpr;
+
+ mutex_lock(&vpr->lock);
+
+ tegra_vpr_buffer_release(buffer);
+
+ if (vpr->num_chunks > 0)
+ tegra_vpr_recycle(vpr);
+
+ mutex_unlock(&vpr->lock);
+}
+
+/*
+ * Prohibit userspace mapping because the CPU cannot access this memory
+ * anyway.
+ */
+static int tegra_vpr_begin_cpu_access(struct dma_buf *buf,
+ enum dma_data_direction direction)
+{
+ return -EPERM;
+}
+
+static int tegra_vpr_end_cpu_access(struct dma_buf *buf,
+ enum dma_data_direction direction)
+{
+ return -EPERM;
+}
+
+static int tegra_vpr_mmap(struct dma_buf *buf, struct vm_area_struct *vma)
+{
+ return -EPERM;
+}
+
+static const struct dma_buf_ops tegra_vpr_buf_ops = {
+ .attach = tegra_vpr_attach,
+ .detach = tegra_vpr_detach,
+ .map_dma_buf = tegra_vpr_map_dma_buf,
+ .unmap_dma_buf = tegra_vpr_unmap_dma_buf,
+ .release = tegra_vpr_release,
+ .begin_cpu_access = tegra_vpr_begin_cpu_access,
+ .end_cpu_access = tegra_vpr_end_cpu_access,
+ .mmap = tegra_vpr_mmap,
+};
+
+static struct dma_buf *tegra_vpr_allocate(struct dma_heap *heap,
+ unsigned long len, u32 fd_flags,
+ u64 heap_flags)
+{
+ struct tegra_vpr *vpr = dma_heap_get_drvdata(heap);
+ struct tegra_vpr_buffer *buffer, *entry;
+ size_t size = ALIGN(len, vpr->align);
+ DEFINE_DMA_BUF_EXPORT_INFO(export);
+ struct dma_buf *buf;
+
+ mutex_lock(&vpr->lock);
+
+ buffer = tegra_vpr_buffer_allocate(vpr, size);
+ if (IS_ERR(buffer)) {
+ mutex_unlock(&vpr->lock);
+ return ERR_CAST(buffer);
+ }
+
+ /* insert in the correct order */
+ if (!list_empty(&vpr->buffers)) {
+ list_for_each_entry(entry, &vpr->buffers, list) {
+ if (buffer->pageno < entry->pageno) {
+ list_add_tail(&buffer->list, &entry->list);
+ break;
+ }
+ }
+ }
+
+ if (list_empty(&buffer->list))
+ list_add_tail(&buffer->list, &vpr->buffers);
+
+ buffer->vpr = vpr;
+
+ /*
+ * If a valid buffer was allocated, wrap it in a dma_buf
+ * and return it.
+ */
+ export.exp_name = dma_heap_get_name(heap);
+ export.ops = &tegra_vpr_buf_ops;
+ export.size = buffer->size;
+ export.flags = fd_flags;
+ export.priv = buffer;
+
+ buf = dma_buf_export(&export);
+ if (IS_ERR(buf))
+ tegra_vpr_buffer_release(buffer);
+
+ mutex_unlock(&vpr->lock);
+ return buf;
+}
+
+static void tegra_vpr_debugfs_show_buffers(struct tegra_vpr *vpr,
+ struct seq_file *s)
+{
+ struct tegra_vpr_buffer *buffer;
+ char buf[16];
+
+ list_for_each_entry(buffer, &vpr->buffers, list) {
+ string_get_size(buffer->size, 1, STRING_UNITS_2, buf,
+ sizeof(buf));
+ seq_printf(s, " %pap-%pap (%s)\n", &buffer->start,
+ &buffer->limit, buf);
+
+ }
+}
+
+static void tegra_vpr_debugfs_show_chunks(struct tegra_vpr *vpr,
+ struct seq_file *s)
+{
+ struct tegra_vpr_buffer *buffer;
+ unsigned int i;
+ char buf[16];
+
+ for (i = 0; i < vpr->num_chunks; i++) {
+ const struct tegra_vpr_chunk *chunk = &vpr->chunks[i];
+
+ string_get_size(chunk->size, 1, STRING_UNITS_2, buf,
+ sizeof(buf));
+ seq_printf(s, " %pap-%pap (%s) (%s, %u buffers)\n",
+ &chunk->start, &chunk->limit, buf,
+ chunk->active ? "active" : "inactive",
+ chunk->num_buffers);
+ }
+
+ list_for_each_entry(buffer, &vpr->buffers, list) {
+ string_get_size(buffer->size, 1, STRING_UNITS_2, buf,
+ sizeof(buf));
+ seq_printf(s, "%pap-%pap (%s, chunks: %*pbl)\n",
+ &buffer->start, &buffer->limit, buf,
+ vpr->num_chunks, buffer->chunks);
+ }
+}
+
+static int tegra_vpr_debugfs_show(struct seq_file *s, struct dma_heap *heap)
+{
+ struct tegra_vpr *vpr = dma_heap_get_drvdata(heap);
+ phys_addr_t limit = vpr->base + vpr->size;
+ char buf[16];
+
+ string_get_size(vpr->size, 1, STRING_UNITS_2, buf, sizeof(buf));
+ seq_printf(s, "%pap-%pap (%s)\n", &vpr->base, &limit, buf);
+
+ if (vpr->num_chunks == 0)
+ tegra_vpr_debugfs_show_buffers(vpr, s);
+ else
+ tegra_vpr_debugfs_show_chunks(vpr, s);
+
+ return 0;
+}
+
+static const struct dma_heap_ops tegra_vpr_heap_ops = {
+ .allocate = tegra_vpr_allocate,
+ .show = tegra_vpr_debugfs_show,
+};
+
+static int tegra_vpr_setup_chunks(struct tegra_vpr *vpr, const char *name)
+{
+ phys_addr_t start, limit;
+ unsigned int order, i;
+ size_t max_size;
+ int err;
+
+ /* This seems a reasonable value, so hard-code this for now. */
+ vpr->num_chunks = 4;
+
+ vpr->chunks = kcalloc(vpr->num_chunks, sizeof(*vpr->chunks),
+ GFP_KERNEL);
+ if (!vpr->chunks)
+ return -ENOMEM;
+
+ max_size = PAGE_SIZE << (get_order(vpr->size) - ilog2(vpr->num_chunks));
+ order = get_order(vpr->align);
+
+ /*
+ * Allocate CMA areas for VPR. All areas will be roughtly the same
+ * size, with the last area taking up the rest.
+ */
+ start = vpr->base;
+ limit = vpr->base + vpr->size;
+
+ pr_debug("VPR: %pap-%pap (%lu pages, %u chunks, %lu MiB)\n", &start,
+ &limit, vpr->num_pages, vpr->num_chunks,
+ (unsigned long)vpr->size / 1024 / 1024);
+
+ for (i = 0; i < vpr->num_chunks; i++) {
+ size_t size = limit - start;
+ phys_addr_t end;
+
+ size = min_t(size_t, size, max_size);
+ end = start + size - 1;
+
+ err = tegra_vpr_chunk_init(vpr, &vpr->chunks[i], start, size,
+ order, name);
+ if (err < 0) {
+ pr_err("failed to create VPR chunk: %d\n", err);
+ goto free;
+ }
+
+ pr_debug(" %2u: %pap-%pap (%lu MiB)\n", i, &start, &end,
+ size / 1024 / 1024);
+ start += size;
+ }
+
+ vpr->first = ~0U;
+ vpr->last = 0U;
+
+ return 0;
+
+free:
+ while (i--)
+ tegra_vpr_chunk_free(&vpr->chunks[i]);
+
+ kfree(vpr->chunks);
+ return err;
+}
+
+static void tegra_vpr_free_chunks(struct tegra_vpr *vpr)
+{
+ unsigned int i;
+
+ for (i = 0; i < vpr->num_chunks; i++)
+ tegra_vpr_chunk_free(&vpr->chunks[i]);
+
+ kfree(vpr->chunks);
+}
+
+static int tegra_vpr_setup_static(struct tegra_vpr *vpr)
+{
+ phys_addr_t start, limit;
+
+ start = vpr->base;
+ limit = vpr->base + vpr->size;
+
+ pr_debug("VPR: %pap-%pap (%lu pages, %lu MiB)\n", &start, &limit,
+ vpr->num_pages, (unsigned long)vpr->size / 1024 / 1024);
+
+ return 0;
+}
+
+static int __init tegra_vpr_add_heap(struct reserved_mem *rmem,
+ struct device_node *np)
+{
+ struct dma_heap_export_info info = {};
+ unsigned long first, last;
+ struct dma_heap *heap;
+ struct tegra_vpr *vpr;
+ int err;
+
+ vpr = kzalloc_obj(*vpr, GFP_KERNEL);
+ if (!vpr)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&vpr->buffers);
+ INIT_LIST_HEAD(&vpr->devices);
+ vpr->resizable = !of_property_read_bool(np, "no-map");
+ vpr->use_freezer = true;
+ vpr->dev_node = np;
+ vpr->align = PAGE_SIZE;
+ vpr->base = rmem->base;
+ vpr->size = rmem->size;
+
+ /* common setup */
+ vpr->start_page = phys_to_page(vpr->base);
+ vpr->num_pages = vpr->size >> PAGE_SHIFT;
+
+ vpr->bitmap = bitmap_zalloc(vpr->num_pages, GFP_KERNEL);
+ if (!vpr->bitmap) {
+ err = -ENOMEM;
+ goto free;
+ }
+
+ first = find_first_bit(vpr->bitmap, vpr->num_pages);
+ last = find_last_bit(vpr->bitmap, vpr->num_pages);
+
+ if (vpr->resizable)
+ err = tegra_vpr_setup_chunks(vpr, rmem->name);
+ else
+ err = tegra_vpr_setup_static(vpr);
+
+ if (err < 0)
+ goto free;
+
+ info.name = vpr->dev_node->name;
+ info.ops = &tegra_vpr_heap_ops;
+ info.priv = vpr;
+
+ heap = dma_heap_add(&info);
+ if (IS_ERR(heap)) {
+ err = PTR_ERR(heap);
+ goto cleanup;
+ }
+
+ rmem->priv = heap;
+
+ return 0;
+
+cleanup:
+ if (vpr->resizable)
+ tegra_vpr_free_chunks(vpr);
+free:
+ bitmap_free(vpr->bitmap);
+ kfree(vpr);
+ return err;
+}
+
+static int __init tegra_vpr_init(void)
+{
+ const char *compatible = "nvidia,tegra-video-protection-region";
+ struct device_node *parent;
+ struct reserved_mem *rmem;
+ int err;
+
+ parent = of_find_node_by_path("/reserved-memory");
+ if (!parent)
+ return 0;
+
+ for_each_child_of_node_scoped(parent, child) {
+ if (!of_device_is_compatible(child, compatible))
+ continue;
+
+ rmem = of_reserved_mem_lookup(child);
+ if (!rmem)
+ continue;
+
+ err = tegra_vpr_add_heap(rmem, child);
+ if (err < 0)
+ pr_err("failed to add VPR heap for %pOF: %d\n", child,
+ err);
+
+ /* only a single VPR heap is supported */
+ break;
+ }
+
+ return 0;
+}
+module_init(tegra_vpr_init);
+
+static int tegra_vpr_node_init(unsigned long offset, struct reserved_mem *rmem)
+{
+ return 0;
+}
+
+static int tegra_vpr_device_init(struct reserved_mem *rmem, struct device *dev)
+{
+ struct dma_heap *heap = rmem->priv;
+ struct tegra_vpr *vpr = dma_heap_get_drvdata(heap);
+ struct tegra_vpr_device *node;
+
+ if (!dev->driver->pm->freeze || !dev->driver->pm->thaw)
+ return -EINVAL;
+
+ node = kzalloc_obj(*node, GFP_KERNEL);
+ if (!node)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&node->node);
+ node->dev = dev;
+
+ list_add_tail(&node->node, &vpr->devices);
+
+ return 0;
+}
+
+static void tegra_vpr_device_release(struct reserved_mem *rmem,
+ struct device *dev)
+{
+ struct dma_heap *heap = rmem->priv;
+ struct tegra_vpr *vpr = dma_heap_get_drvdata(heap);
+ struct tegra_vpr_device *node, *tmp;
+
+ list_for_each_entry_safe(node, tmp, &vpr->devices, node) {
+ if (node->dev == dev) {
+ list_del(&node->node);
+ kfree(node);
+ }
+ }
+}
+
+static const struct reserved_mem_ops tegra_vpr_rmem_ops = {
+ .node_init = tegra_vpr_node_init,
+ .device_init = tegra_vpr_device_init,
+ .device_release = tegra_vpr_device_release,
+};
+
+RESERVEDMEM_OF_DECLARE(tegra_vpr, "nvidia,tegra-video-protection-region",
+ &tegra_vpr_rmem_ops);
+
+MODULE_DESCRIPTION("NVIDIA Tegra Video-Protection-Region DMA-BUF heap driver");
+MODULE_LICENSE("GPL");
diff --git a/include/trace/events/tegra_vpr.h b/include/trace/events/tegra_vpr.h
new file mode 100644
index 000000000000..f8ceb17679fe
--- /dev/null
+++ b/include/trace/events/tegra_vpr.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#if !defined(_TRACE_TEGRA_VPR_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_TEGRA_VPR_H
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM tegra_vpr
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(tegra_vpr_chunk_activate,
+ TP_PROTO(phys_addr_t start, phys_addr_t limit),
+ TP_ARGS(start, limit),
+ TP_STRUCT__entry(
+ __field(phys_addr_t, start)
+ __field(phys_addr_t, limit)
+ ),
+ TP_fast_assign(
+ __entry->start = start;
+ __entry->limit = limit;
+ ),
+ TP_printk("%pap-%pap", &__entry->start,
+ &__entry->limit)
+);
+
+TRACE_EVENT(tegra_vpr_chunk_deactivate,
+ TP_PROTO(phys_addr_t start, phys_addr_t limit),
+ TP_ARGS(start, limit),
+ TP_STRUCT__entry(
+ __field(phys_addr_t, start)
+ __field(phys_addr_t, limit)
+ ),
+ TP_fast_assign(
+ __entry->start = start;
+ __entry->limit = limit;
+ ),
+ TP_printk("%pap-%pap", &__entry->start,
+ &__entry->limit)
+);
+
+TRACE_EVENT(tegra_vpr_set,
+ TP_PROTO(phys_addr_t base, phys_addr_t size),
+ TP_ARGS(base, size),
+ TP_STRUCT__entry(
+ __field(phys_addr_t, start)
+ __field(phys_addr_t, limit)
+ ),
+ TP_fast_assign(
+ __entry->start = base;
+ __entry->limit = base + size;
+ ),
+ TP_printk("%pap-%pap", &__entry->start, &__entry->limit)
+);
+
+#endif /* _TRACE_TEGRA_VPR_H */
+
+#include <trace/define_trace.h>
--
2.54.0
^ permalink raw reply related
* [PATCH v3 09/11] arm64: tegra: Add VPR placeholder node on Tegra234
From: Thierry Reding @ 2026-07-01 16:08 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
T.J. Mercier, Christian König, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon
Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
linaro-mm-sig, linux-trace-kernel, Thierry Reding
In-Reply-To: <20260701-tegra-vpr-v3-0-d80f7b871bb4@nvidia.com>
From: Thierry Reding <treding@nvidia.com>
This node contains two sets of properties, one for the case where the
VPR is resizable (in which case the VPR region will be dynamically
allocated at boot time) and another case where the VPR is fixed in size
and initialized by early firmware.
The firmware running on the device is responsible for updating the node
with the real physical address for the fixed VPR case and remove the
properties needed only for resizable VPR. Similarly, if the VPR is
resizable, the firmware should remove the "reg" property since it is no
longer needed.
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v3:
- comment out fixed VPR properties, assume resizable by default
- rename node to "protected"
---
arch/arm64/boot/dts/nvidia/tegra234.dtsi | 39 ++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/arch/arm64/boot/dts/nvidia/tegra234.dtsi b/arch/arm64/boot/dts/nvidia/tegra234.dtsi
index 8e0c51e496e2..52ff11873580 100644
--- a/arch/arm64/boot/dts/nvidia/tegra234.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra234.dtsi
@@ -29,6 +29,45 @@ aliases {
i2c8 = &dp_aux_ch3_i2c;
};
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ vpr: protected {
+ compatible = "nvidia,tegra-video-protection-region";
+ status = "disabled";
+
+ /*
+ * Two variants exist for this. For fixed VPR, the
+ * firmware is supposed to update the "reg" property
+ * with the fixed memory region configured as VPR.
+ *
+ * For resizable VPR we don't care about the exact
+ * address and instead want a reserved region to be
+ * allocated with a certain size and alignment at
+ * boot time.
+ *
+ * The below assumes resizable VPR by default. If the
+ * firmwares sets up fixed VPR, it is responsible for
+ * adding the missing "reg" property, removing any of
+ * the unused properties, as well as adding a unit-
+ * address matching the "reg" property.
+ */
+
+ /* fixed VPR */
+ /*
+ reg = <0x0 0x0 0x0 0x0>;
+ no-map;
+ */
+
+ /* resizable VPR */
+ size = <0x0 0x70000000>;
+ alignment = <0x0 0x100000>;
+ reusable;
+ };
+ };
+
bus@0 {
compatible = "simple-bus";
--
2.54.0
^ permalink raw reply related
* [PATCH v3 10/11] arm64: tegra: Hook up VPR to host1x
From: Thierry Reding @ 2026-07-01 16:08 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
T.J. Mercier, Christian König, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon
Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
linaro-mm-sig, linux-trace-kernel, Thierry Reding
In-Reply-To: <20260701-tegra-vpr-v3-0-d80f7b871bb4@nvidia.com>
From: Thierry Reding <treding@nvidia.com>
The host1x needs access to the VPR region, so make sure to reference it
via the memory-region property.
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
arch/arm64/boot/dts/nvidia/tegra234.dtsi | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm64/boot/dts/nvidia/tegra234.dtsi b/arch/arm64/boot/dts/nvidia/tegra234.dtsi
index 52ff11873580..38637e8e6fc9 100644
--- a/arch/arm64/boot/dts/nvidia/tegra234.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra234.dtsi
@@ -4479,6 +4479,9 @@ vic@15340000 {
interconnect-names = "dma-mem", "write";
iommus = <&smmu_niso1 TEGRA234_SID_VIC>;
dma-coherent;
+
+ memory-region = <&vpr>;
+ memory-region-names = "protected";
};
nvdec@15480000 {
@@ -4497,6 +4500,9 @@ nvdec@15480000 {
iommus = <&smmu_niso1 TEGRA234_SID_NVDEC>;
dma-coherent;
+ memory-region = <&vpr>;
+ memory-region-names = "protected";
+
nvidia,memory-controller = <&mc>;
/*
--
2.54.0
^ permalink raw reply related
* [PATCH v3 11/11] arm64: tegra: Add VPR placeholder node on Tegra264
From: Thierry Reding @ 2026-07-01 16:08 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
T.J. Mercier, Christian König, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon
Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
linaro-mm-sig, linux-trace-kernel, Thierry Reding
In-Reply-To: <20260701-tegra-vpr-v3-0-d80f7b871bb4@nvidia.com>
From: Thierry Reding <treding@nvidia.com>
This node contains two sets of properties, one for the case where the
VPR is resizable (in which case the VPR region will be dynamically
allocated at boot time) and another case where the VPR is fixed in size
and initialized by early firmware.
The firmware running on the device is responsible for updating the node
with the real physical address for the fixed VPR case and remove the
properties needed only for resizable VPR. Similarly, if the VPR is
resizable, the firmware should remove the "reg" property since it is no
longer needed.
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v3:
- comment out fixed VPR properties, assume resizable by default
- rename node to "protected"
---
arch/arm64/boot/dts/nvidia/tegra264.dtsi | 33 ++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/arch/arm64/boot/dts/nvidia/tegra264.dtsi b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
index 4c701abd25a8..85a18d99d643 100644
--- a/arch/arm64/boot/dts/nvidia/tegra264.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
@@ -24,6 +24,39 @@ shmem_bpmp: shmem@86070000 {
reg = <0x0 0x86070000 0x0 0x2000>;
no-map;
};
+
+ vpr: protected {
+ compatible = "nvidia,tegra-video-protection-region";
+ status = "disabled";
+
+ /*
+ * Two variants exist for this. For fixed VPR, the
+ * firmware is supposed to update the "reg" property
+ * with the fixed memory region configured as VPR.
+ *
+ * For resizable VPR we don't care about the exact
+ * address and instead want a reserved region to be
+ * allocated with a certain size and alignment at
+ * boot time.
+ *
+ * The below assumes resizable VPR by default. If the
+ * firmwares sets up fixed VPR, it is responsible for
+ * adding the missing "reg" property, removing any of
+ * the unused properties, as well as adding a unit-
+ * address matching the "reg" property.
+ */
+
+ /* fixed VPR */
+ /*
+ reg = <0x0 0x0 0x0 0x0>;
+ no-map;
+ */
+
+ /* resizable VPR */
+ size = <0x0 0x70000000>;
+ alignment = <0x0 0x100000>;
+ reusable;
+ };
};
/* SYSTEM MMIO */
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v8 17/46] KVM: guest_memfd: Advertise KVM_SET_MEMORY_ATTRIBUTES2 ioctl
From: Sean Christopherson @ 2026-07-01 16:09 UTC (permalink / raw)
To: Xiaoyao Li
Cc: ackerleytng, aik, andrew.jones, binbin.wu, brauner, chao.p.peng,
david, jmattson, jthoughton, michael.roth, oupton, pankaj.gupta,
qperret, rick.p.edgecombe, rientjes, shivankg, steven.price,
tabba, willy, wyihan, yan.y.zhao, forkloop, pratyush,
suzuki.poulose, aneesh.kumar, liam, Paolo Bonzini,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Shuah Khan, Shuah Khan,
Vishal Annapurve, Andrew Morton, Chris Li, Kairui Song,
Kemeng Shi, Nhat Pham, Barry Song, Axel Rasmussen, Yuanchu Xie,
Wei Xu, Youngjun Park, Qi Zheng, Shakeel Butt, Kiryl Shutsemau,
Baoquan He, Jason Gunthorpe, Vlastimil Babka, kvm, linux-kernel,
linux-trace-kernel, linux-doc, linux-kselftest, linux-mm,
linux-coco
In-Reply-To: <584a8f9a-1538-4f8b-b576-75ef0fa961c7@intel.com>
On Wed, Jul 01, 2026, Xiaoyao Li wrote:
> On 6/19/2026 8:31 AM, Ackerley Tng via B4 Relay wrote:
> > @@ -4969,6 +4973,11 @@ static int kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg)
> > return 1;
> > case KVM_CAP_GUEST_MEMFD_FLAGS:
> > return kvm_gmem_get_supported_flags(kvm);
> > + case KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES:
> > + if (!gmem_in_place_conversion || !kvm_supports_private_mem(kvm))
> > + return 0;
> > +
> > + return KVM_MEMORY_ATTRIBUTE_PRIVATE;
> > #endif
> > default:
> > break;
>
> this looks inconsistent with the
>
> case KVM_SET_MEMORY_ATTRIBUTES2:
> if (!gmem_in_place_conversion)
> return -ENOTTY;
>
> Well, the check of
>
> if (!kvm_arch_has_private_mem(f->kvm))
> return -EINVAL;
>
> is buried in the following kvm_gmem_set_attributes(). How about moving of
> kvm_arch_has_private_mem() check to put it along with
> gmem_in_place_conversion check in kvm_gmem_ioctl() in Patch 13?
Me confused, patch 13 already adds the kvm_arch_has_private_mem() in
kvm_gmem_set_attributes().
That said, the ordering here is wonky and misleading. A cursory read of the series
would make one think that waiting to advertise KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES
makes it safe/ok for KVM to plumb in support for KVM_SET_MEMORY_ATTRIBUTES2 over
multiple patches. But that's not actually true, because the ioctl becomes live
the instant the code exists, userspace doesn't need to wait for KVM to formally
advertise support.
To further confuse matters, it is actually safe/ok to iteratively add support,
because it's all effectively dead code until "Let userspace disable per-VM mem
attributes, enable per-gmem attributes".
So, I think we should go a step further than what I think Xiaoyao is suggesting,
and fully squash patch 17 into patch 13. That way the reader doesn't have to jump
through as many mental hoops to piece together what is happening. It'll obviously
be a bigger patch, but should be easier to review/understand overall.
Oh, and that combined patch should carve out error_offset straightaway, so that
the full uAPI can be reviewed in a single patch.
^ permalink raw reply
* Re: [PATCH 2/2] tracing: Keep pid and comm[] in the same structure
From: David Laight @ 2026-07-01 16:52 UTC (permalink / raw)
To: Steven Rostedt
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, Michal Koutný
In-Reply-To: <20260701082340.5b0bd4e7@gandalf.local.home>
On Wed, 1 Jul 2026 08:23:40 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:
> On Wed, 1 Jul 2026 13:04:04 +0100
> David Laight <david.laight.linux@gmail.com> wrote:
>
>
> > > Well, that would break trace-cmd. As reading the raw buffers clears the
> > > trace, and trace-cmd reads the saved_cmdlines file *after* it reads the
> > > trace, as during the trace it gets populated.
> >
> > So you'd need to clear it when tracing is enabled after the buffer is cleared.
> > Just a matter of getting the timing right.
>
> Why? Note, saved_cmdlines is for *all* instances. You can have multiple
> instances tracing different things and they still all use the one
> saved_cmdlines file. It's not tied to any specific buffer. It's a cache. It
> gets populated at the next schedule switch after an event occurs.
>
Mostly thoughts about the size of the cache and the difference between the
apparent default size of 128 and the actual size of 6300.
I had said that I'd not really looked at when it was used, just what
it contained.
None of that is relevant to this patch which just changes the data structures
a bit.
It also means that most updates will only dirty a single cache line.
Indeed it is probably worth comparing the comm[] strings and only doing
the write it they differ to avoid dirtying the cache line(s).
David
^ permalink raw reply
* Re: [PATCH v8 06/46] KVM: Enumerate support for PRIVATE memory iff kvm_arch_has_private_mem is defined
From: Sean Christopherson @ 2026-07-01 16:55 UTC (permalink / raw)
To: Xiaoyao Li
Cc: ackerleytng, aik, andrew.jones, binbin.wu, brauner, chao.p.peng,
david, jmattson, jthoughton, michael.roth, oupton, pankaj.gupta,
qperret, rick.p.edgecombe, rientjes, shivankg, steven.price,
tabba, willy, wyihan, yan.y.zhao, forkloop, pratyush,
suzuki.poulose, aneesh.kumar, liam, Paolo Bonzini,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H. Peter Anvin, Steven Rostedt, Masami Hiramatsu,
Mathieu Desnoyers, Jonathan Corbet, Shuah Khan, Shuah Khan,
Vishal Annapurve, Andrew Morton, Chris Li, Kairui Song,
Kemeng Shi, Nhat Pham, Barry Song, Axel Rasmussen, Yuanchu Xie,
Wei Xu, Youngjun Park, Qi Zheng, Shakeel Butt, Kiryl Shutsemau,
Baoquan He, Jason Gunthorpe, Vlastimil Babka, kvm, linux-kernel,
linux-trace-kernel, linux-doc, linux-kselftest, linux-mm,
linux-coco
In-Reply-To: <739e6834-40b3-405b-ada4-d31c38d8416a@intel.com>
On Wed, Jul 01, 2026, Xiaoyao Li wrote:
> On 6/19/2026 8:31 AM, Ackerley Tng via B4 Relay wrote:
> > From: Ackerley Tng <ackerleytng@google.com>
> >
> > Explicitly guard reporting support for KVM_MEMORY_ATTRIBUTE_PRIVATE based
> > on kvm_arch_has_private_mem being #defined in anticipation of decoupling
> > kvm_supported_mem_attributes() from CONFIG_KVM_VM_MEMORY_ATTRIBUTES.
>
> Well, after this series, kvm_supported_mem_attributes() is renamed to
> kvm_supported_vm_mem_attributes(), and it's still under
> CONFIG_KVM_VM_MEMORY_ATTRIBUTES.
>
> > guest_memfd support for memory attributes will be unconditional to avoid
> > yet more macros (all architectures that support guest_memfd are expected to
> > use per-gmem attributes at some point), at which point enumerating support
> > KVM_MEMORY_ATTRIBUTE_PRIVATE based solely on memory attributes being
> > supported _somewhere_ would result in KVM over-reporting support on arm64.
>
> I don't understand it. This patch only changes the behavior of
> kvm_supported_mem_attributes(), the usage of which is guarded by
> CONFIG_KVM_VM_MEMORY_ATTRIBUTESq. This is config is only visible to x86 due
> to patch 03. How does it affect arm64?
Hrm, yeah, this is messed up. Ahh, I think Ackerley shuffled things around and
"broke" stuff in the process. In v7[1] and earlier, the diff was this:
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 091f201251159..68142bc962953 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -722,7 +722,7 @@ static inline int kvm_arch_vcpu_memslots_id(struct kvm_vcpu *vcpu)
}
#endif
-#ifndef CONFIG_KVM_VM_MEMORY_ATTRIBUTES
+#ifndef kvm_arch_has_private_mem
static inline bool kvm_arch_has_private_mem(struct kvm *kvm)
{
return false;
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 306153abbafa5..abb9cfa3eb04d 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -2421,8 +2421,10 @@ static int kvm_vm_ioctl_clear_dirty_log(struct kvm *kvm,
#ifdef CONFIG_KVM_VM_MEMORY_ATTRIBUTES
static u64 kvm_supported_mem_attributes(struct kvm *kvm)
{
+#ifdef kvm_arch_has_private_mem
if (!kvm || kvm_arch_has_private_mem(kvm))
return KVM_MEMORY_ATTRIBUTE_PRIVATE;
+#endif
return 0;
}
which makes a *lot* more sense given the changelog (and IMO for the ordering in
general). In v8 here, Ackerley combined part of a change[2] (that I provided
off-list) with part of this commit, to create patch 4, "KVM: Decouple
kvm_has_arch_private_mem from CONFIG_KVM_VM_MEMORY_ATTRIBUTESthe".
Ackerley, the cover letter says:
+ Reshuffled the earlier commits that deal with preparing KVM to stop
seeing VM memory attributes as the only source of attributes.
but there's no explanation for *why* the reshuffling was done. Reorganizing
code like this at v8 of a series this size is a big "no-no" unless there's a
*really* good reason to do so. In addition to the resulting confusion, changes
like this invalidate Fuad's Reviewed-by. And since it's obviously quite difficult
to tease out exactly what changed, it's not realistic to re-review things without
doing a deep audit of the series, which no one wants to do for a series that is/was
so close to being fully ready. And without such an audit, I can't accept the
patches, because I can't trust that what I am accepting is what I and others have
reviewed.
So, except where there is/was a *need* to shuffle things around relative to v7,
I think we should revert back to the v7 ordering for v9. And where there is a
need to rework things, each and every one of those needs to be explicitly
documented, because "Reshuffled the earlier commits" is grossly insufficient.
[1] https://lore.kernel.org/all/20260522-gmem-inplace-conversion-v7-3-2f0fae496530@google.com
[2] https://github.com/sean-jc/linux/commit/8a475b1bcf89f1cf776ed9ce7d6bb587aab0d421
^ permalink raw reply related
* [PATCH] tracing: Add a no-rcu-check version of trace_##event##_enabled()
From: Steven Rostedt @ 2026-07-01 17:27 UTC (permalink / raw)
To: LKML, Linux trace kernel
Cc: Masami Hiramatsu, Mathieu Desnoyers, Geert Uytterhoeven,
David Carlier
From: Steven Rostedt <rostedt@goodmis.org>
Tracepoints require that RCU is watching. To prevent them from being used
in places that RCU is not watching, the trace_##event() macro always
calls rcu_is_watching() even when the event is not enabled and warns if
RCU is not watching. This is to make sure a warning is triggered even if
the tracepoint is never enabled (as it is only a bug when it is).
It was noticed that tracepoints could be hidden within
trace_#event#_enabled() calls, which are used to do extra work for the
tracepoint only if the tracepoint is enabled. But this also can hide the
fact that a tracepoint is placed in a location that can be called when RCU
is not watching.
Commit 9764e731ef6ab ("tracepoint: Add lockdep rcu_is_watching() check to
trace_##name##_enabled()") added a check to the trace_##event##_enabled()
macro to make sure RCU is watching when it is called to make sure not to
hide the bug of a tracepoint being called when RCU is not watching.
There is one case in the irq_disable tracepoint where it is within a
trace_irq_disable_enabled() block, but it checks if RCU is watching, and
if it isn't, it makes a call to ct_irq_enter() that makes RCU watch again.
But because trace_irq_disable_enabled() now checks if RCU is watching and
will trigger if it isn't. This is a false warning as the code within
the block handles this case.
Add a new internal macro __trace_##event##_enabled() that doesn't check if
RCU is watching, and convert the irq_enable/disable tracepoints over to
it.
Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>
Closes: https://lore.kernel.org/all/CAMuHMdXud_RpWag_hFqa2ByBGRxg6KnxGL1ObCWZrpTsk3TfAw@mail.gmail.com/
Fixes: 9764e731ef6ab ("tracepoint: Add lockdep rcu_is_watching() check to trace_##name##_enabled()")
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
---
include/linux/tracepoint.h | 12 +++++++++++-
kernel/trace/trace_preemptirq.c | 2 +-
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h
index 4a0c36f40fe2..e0d838c9ce93 100644
--- a/include/linux/tracepoint.h
+++ b/include/linux/tracepoint.h
@@ -292,13 +292,18 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
{ \
} \
static inline bool \
+ __trace_##name##_enabled(void) \
+ { \
+ return static_branch_unlikely(&__tracepoint_##name.key);\
+ } \
+ static inline bool \
trace_##name##_enabled(void) \
{ \
if (IS_ENABLED(CONFIG_LOCKDEP)) { \
WARN_ONCE(!rcu_is_watching(), \
"RCU not watching for tracepoint"); \
} \
- return static_branch_unlikely(&__tracepoint_##name.key);\
+ return __trace_##name##_enabled(); \
}
#define __DECLARE_TRACE(name, proto, args, cond, data_proto) \
@@ -457,6 +462,11 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
{ \
} \
static inline bool \
+ __trace_##name##_enabled(void) \
+ { \
+ return false; \
+ } \
+ static inline bool \
trace_##name##_enabled(void) \
{ \
return false; \
diff --git a/kernel/trace/trace_preemptirq.c b/kernel/trace/trace_preemptirq.c
index 0c42b15c3800..b63e3558948f 100644
--- a/kernel/trace/trace_preemptirq.c
+++ b/kernel/trace/trace_preemptirq.c
@@ -30,7 +30,7 @@
#else
#define trace(point, args) \
do { \
- if (trace_##point##_enabled()) { \
+ if (__trace_##point##_enabled()) { \
bool exit_rcu = false; \
if (in_nmi()) \
break; \
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v13 04/11] perf/probe: Ignore comment lines in dynamic_events/kprobe_events file
From: Namhyung Kim @ 2026-07-01 17:44 UTC (permalink / raw)
To: Masami Hiramatsu
Cc: Arnaldo Carvalho de Melo, Steven Rostedt, Mathieu Desnoyers,
Jonathan Corbet, Shuah Khan, linux-kernel, linux-trace-kernel,
linux-doc, linux-kselftest
In-Reply-To: <20260701073939.81842d6f6bd139a329c45c7f@kernel.org>
On Wed, Jul 01, 2026 at 07:39:39AM +0900, Masami Hiramatsu wrote:
> On Mon, 29 Jun 2026 17:33:43 -0700
> Namhyung Kim <namhyung@kernel.org> wrote:
>
> > Hi Masami,
> >
> > On Tue, Jun 30, 2026 at 07:32:11AM +0900, Masami Hiramatsu wrote:
> > > Hi Arnaldo, Namhyung,
> > >
> > > I forgot to CC this. Can I pick this patch via linux-trace tree,
> > > or would you pick this?
> > > This is a part of typecast series [1] only for debugging.
> >
> > Thanks for letting me know.
> >
> > I think it's better to route this through the perf tree as we're seeing
> > a lot of cleanups all around the code base. Having this together would
> > reduce chances of future conflicts. Does that sound ok to you?
>
> OK, thanks for confirmation. Then I'll drop it from probes/for-next (and probes/core).
Thanks, I've applied it to perf-tools-next.
Best regards,
Namhyung
^ permalink raw reply
* Re: [RFC PATCH 2/4] tracing/probes: Compile all fetchargs into a single BPF program per event
From: Alexei Starovoitov @ 2026-07-01 18:41 UTC (permalink / raw)
To: Masami Hiramatsu (Google), Steven Rostedt, Shuah Khan
Cc: Mathieu Desnoyers, linux-kernel, linux-trace-kernel,
linux-kselftest, bpf
In-Reply-To: <178291354144.1566898.14374948740441958770.stgit@devnote2>
On Wed Jul 1, 2026 at 6:45 AM PDT, Masami Hiramatsu (Google) wrote:
> +static bool trace_probe_can_compile_bpf(struct trace_probe *tp)
> +{
> + int i;
> +
> + if (tp->nr_args == 0)
> + return false;
> +
> + for (i = 0; i < tp->nr_args; i++) {
> + struct probe_arg *parg = &tp->args[i];
> + struct fetch_insn *code = parg->code;
> +
> + while (code->op != FETCH_OP_END) {
> + switch (code->op) {
> + case FETCH_OP_REG:
> + case FETCH_OP_IMM:
> + case FETCH_OP_DEREF:
> + case FETCH_OP_ST_RAW:
> + case FETCH_OP_ST_MEM:
> + break;
> + case FETCH_OP_ARG:
> + if (regs_get_kernel_argument_offset(code->param) < 0)
> + return false;
> + break;
> + default:
> + return false;
> + }
> + code++;
> + }
> + }
> + return true;
> +}
> +
> +static void trace_probe_compile_bpf(struct trace_probe *tp)
> +{
> + struct bpf_insn *insns;
> + int i = 0;
> + struct bpf_prog *prog;
> + int err, idx;
> +
> + if (!trace_probe_can_compile_bpf(tp))
> + return;
> +
> + insns = kmalloc_array(512, sizeof(struct bpf_insn), GFP_KERNEL);
> + if (!insns)
> + return;
> +
> + /* Prologue: R6 = ctx */
> + insns[i++] = BPF_MOV64_REG(BPF_REG_6, BPF_REG_1);
> + /* R7 = ctx->rec */
> + insns[i++] = BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_6,
> + offsetof(struct fetch_bpf_ctx, rec));
> + /* R8 = ctx->data */
> + insns[i++] = BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_6,
> + offsetof(struct fetch_bpf_ctx, data));
> + /* R9 = total size (0) */
> + insns[i++] = BPF_MOV64_IMM(BPF_REG_9, 0);
> +
> + for (idx = 0; idx < tp->nr_args; idx++) {
> + struct probe_arg *parg = &tp->args[idx];
> + struct fetch_insn *code = parg->code;
> +
> + while (code->op != FETCH_OP_END && i < 500) {
> + switch (code->op) {
> + case FETCH_OP_REG:
> + /* R0 = *(unsigned long *)(R7 + code->param) */
> + insns[i++] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_7, code->param);
> + break;
> + case FETCH_OP_ARG: {
> + int offset = regs_get_kernel_argument_offset(code->param);
> + /* R0 = *(unsigned long *)(R7 + offset) */
> + insns[i++] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_7, offset);
> + break;
> + }
> + case FETCH_OP_IMM:
> + insns[i++] = BPF_LD_IMM64(BPF_REG_0, code->immediate);
> + break;
> + case FETCH_OP_DEREF:
> + /* Add offset: R3 = R0 + code->offset (src) */
> + insns[i++] = BPF_MOV64_REG(BPF_REG_2, BPF_REG_0);
> + if (code->offset)
> + insns[i++] = BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
> + code->offset);
> + /* R1 = dst (R10 - 8 on stack) */
> + insns[i++] = BPF_MOV64_REG(BPF_REG_1, BPF_REG_10);
> + insns[i++] = BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8);
> + /* R3 = size */
> + insns[i++] = BPF_MOV64_IMM(BPF_REG_3, sizeof(unsigned long));
> + /* Call copy_from_kernel_nofault(dst, src, size) */
> + insns[i++] = BPF_EMIT_CALL(copy_from_kernel_nofault);
> + /* if (R0 < 0) return R0; */
> + insns[i++] = BPF_JMP_IMM(BPF_JSGE, BPF_REG_0, 0, 1);
> + insns[i++] = BPF_EXIT_INSN();
> + /* R0 = *(unsigned long *)(R10 - 8) */
> + insns[i++] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8);
> + break;
> + case FETCH_OP_ST_RAW:
> + /* Store R0 into R8 (data) + parg->offset based on size */
> + switch (code->size) {
> + case 1:
> + insns[i++] = BPF_STX_MEM(BPF_B, BPF_REG_8, BPF_REG_0,
> + parg->offset);
> + break;
> + case 2:
> + insns[i++] = BPF_STX_MEM(BPF_H, BPF_REG_8, BPF_REG_0,
> + parg->offset);
> + break;
> + case 4:
> + insns[i++] = BPF_STX_MEM(BPF_W, BPF_REG_8, BPF_REG_0,
> + parg->offset);
> + break;
> + case 8:
> + insns[i++] = BPF_STX_MEM(BPF_DW, BPF_REG_8, BPF_REG_0,
> + parg->offset);
> + break;
> + }
> + break;
> + case FETCH_OP_ST_MEM:
> + /* Add offset: R2 = R0 + code->offset (src) */
> + insns[i++] = BPF_MOV64_REG(BPF_REG_2, BPF_REG_0);
> + if (code->offset)
> + insns[i++] = BPF_ALU64_IMM(BPF_ADD, BPF_REG_2,
> + code->offset);
> + /* R1 = dst (R8 + parg->offset) */
> + insns[i++] = BPF_MOV64_REG(BPF_REG_1, BPF_REG_8);
> + if (parg->offset)
> + insns[i++] = BPF_ALU64_IMM(BPF_ADD, BPF_REG_1,
> + parg->offset);
> + /* R3 = size */
> + insns[i++] = BPF_MOV64_IMM(BPF_REG_3, code->size);
> + /* Call copy_from_kernel_nofault(dst, src, size) */
> + insns[i++] = BPF_EMIT_CALL(copy_from_kernel_nofault);
> + /* if (R0 < 0) return R0; */
> + insns[i++] = BPF_JMP_IMM(BPF_JSGE, BPF_REG_0, 0, 1);
> + insns[i++] = BPF_EXIT_INSN();
> + break;
> + default:
> + goto out;
> + }
> + code++;
> + }
> + }
Nack.
I really don't like it.
There were days in the past when the kernel generating bpf directly was appealing.
These days are gone. Performance improvements for fetchargs is not a good reason
to add all this complexity and bypass verifier checks.
bpf insns should come from user space.
^ permalink raw reply
* Re: [RFC PATCH 2/4] tracing/probes: Compile all fetchargs into a single BPF program per event
From: Steven Rostedt @ 2026-07-01 18:47 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Masami Hiramatsu (Google), Shuah Khan, Mathieu Desnoyers,
linux-kernel, linux-trace-kernel, linux-kselftest, bpf
In-Reply-To: <DJNGE5JG5ZY4.FEO5DZ21TL7Z@gmail.com>
On Wed, 01 Jul 2026 11:41:26 -0700
"Alexei Starovoitov" <alexei.starovoitov@gmail.com> wrote:
> Nack.
> I really don't like it.
So the nack is mostly your opinion and not technical?
> There were days in the past when the kernel generating bpf directly was appealing.
> These days are gone. Performance improvements for fetchargs is not a good reason
Why is performance *not* a good reason?
> to add all this complexity and bypass verifier checks.
The code lives in the kernel. What reason is there to add verification
checks? The point of verification is because we don't trust user space. Why
do we not trust the kernel?
> bpf insns should come from user space.
Why?
-- Steve
^ permalink raw reply
* Re: [RFC PATCH 2/4] tracing/probes: Compile all fetchargs into a single BPF program per event
From: Alexei Starovoitov @ 2026-07-01 18:53 UTC (permalink / raw)
To: Steven Rostedt
Cc: Masami Hiramatsu (Google), Shuah Khan, Mathieu Desnoyers,
linux-kernel, linux-trace-kernel, linux-kselftest, bpf
In-Reply-To: <20260701144726.0a5f2204@gandalf.local.home>
On Wed Jul 1, 2026 at 11:47 AM PDT, Steven Rostedt wrote:
> On Wed, 01 Jul 2026 11:41:26 -0700
> "Alexei Starovoitov" <alexei.starovoitov@gmail.com> wrote:
>
>> Nack.
>> I really don't like it.
>
> So the nack is mostly your opinion and not technical?
>
>> There were days in the past when the kernel generating bpf directly was appealing.
>> These days are gone. Performance improvements for fetchargs is not a good reason
>
> Why is performance *not* a good reason?
>
>> to add all this complexity and bypass verifier checks.
>
> The code lives in the kernel. What reason is there to add verification
> checks? The point of verification is because we don't trust user space. Why
> do we not trust the kernel?
>
>> bpf insns should come from user space.
>
> Why?
because I see this patchset as pointless kernel bloat with the "help" of bpf.
Hence the nack to avoid dragging bpf into this.
Call it philosophical disagreement if you like.
^ permalink raw reply
* Re: [PATCH v4 12/13] rv: Fix read_lock scope in per-task DA cleanup
From: Steven Rostedt @ 2026-07-01 19:27 UTC (permalink / raw)
To: Gabriele Monaco; +Cc: linux-kernel, linux-trace-kernel, Wen Yang, Nam Cao
In-Reply-To: <20260601153840.124372-13-gmonaco@redhat.com>
On Mon, 1 Jun 2026 17:38:39 +0200
Gabriele Monaco <gmonaco@redhat.com> wrote:
> The da_monitor_reset_all() function for per-task monitors takes
> tasklist_lock while iterating over tasks, then keeps it also while
> iterating over idle tasks (one per CPU). The latter is not necessary
> since the lock needs to guard only for_each_process_thread().
>
> Use a scoped_guard for more compact syntax and adjust the scope only
> where the lock is necessary.
>
> Reviewed-by: Wen Yang <wen.yang@linux.dev>
> Reviewed-by: Nam Cao <namcao@linutronix.de>
> Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
Hmm, this and patch 13 didn't get applied. Do you plan on sending them later?
-- Steve
^ permalink raw reply
* Re: [PATCH v3 01/11] dt-bindings: reserved-memory: Document Tegra VPR
From: Rob Herring (Arm) @ 2026-07-01 19:53 UTC (permalink / raw)
To: Thierry Reding
Cc: Christian Borntraeger, Rasmus Villemoes, dri-devel,
David Hildenbrand, Yury Norov, linux-media, linux-kernel,
Robin Murphy, Simona Vetter, linux-trace-kernel,
Krzysztof Kozlowski, Christian König, linux-mm, Russell King,
Will Deacon, Masami Hiramatsu, David Airlie, Vasily Gorbik,
Benjamin Gaignard, linaro-mm-sig, Heiko Carstens, Sumit Semwal,
Thierry Reding, Maxime Ripard, Thierry Reding, John Stultz,
Luca Ceresoli, Vlastimil Babka, Brian Starkey, Mikko Perttunen,
Michal Hocko, Steven Rostedt, Jonathan Hunter, Maarten Lankhorst,
Sowjanya Komatineni, Suren Baghdasaryan, linux-arm-kernel,
linux-s390, devicetree, Liam R. Howlett, linux-tegra,
Catalin Marinas, Marek Szyprowski, Conor Dooley,
Thomas Zimmermann, Andrew Morton, Gerald Schaefer,
Alexander Gordeev, Lorenzo Stoakes, T.J. Mercier,
Mathieu Desnoyers, iommu, Mike Rapoport, Sven Schnelle
In-Reply-To: <20260701-tegra-vpr-v3-1-d80f7b871bb4@nvidia.com>
On Wed, 01 Jul 2026 18:08:12 +0200, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> The Video Protection Region (VPR) found on NVIDIA Tegra chips is a
> region of memory that is protected from CPU accesses. It is used to
> decode and play back DRM protected content.
>
> It is a standard reserved memory region that can exist in two forms:
> static VPR where the base address and size are fixed (uses the "reg"
> property to describe the memory) and a resizable VPR where only the
> size is known upfront and the OS can allocate it wherever it can be
> accomodated.
>
> Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
> Changes in v2:
> - add examples for fixed and resizable VPR
> ---
> .../nvidia,tegra-video-protection-region.yaml | 76 ++++++++++++++++++++++
> 1 file changed, 76 insertions(+)
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/reserved-memory/nvidia,tegra-video-protection-region.example.dtb: protected@2a8000000 (nvidia,tegra-video-protection-region): reg: [[2, 2818572288], [0, 1879048192]] is too long
from schema $id: http://devicetree.org/schemas/reserved-memory/nvidia,tegra-video-protection-region.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/reserved-memory/nvidia,tegra-video-protection-region.example.dtb: protected@2a8000000 (nvidia,tegra-video-protection-region): Unevaluated properties are not allowed ('no-map', 'reg' were unexpected)
from schema $id: http://devicetree.org/schemas/reserved-memory/nvidia,tegra-video-protection-region.yaml
doc reference errors (make refcheckdocs):
See https://patchwork.kernel.org/project/devicetree/patch/20260701-tegra-vpr-v3-1-d80f7b871bb4@nvidia.com
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
^ permalink raw reply
* Re: [PATCH v3 02/11] dt-bindings: display: tegra: Document memory regions
From: Rob Herring (Arm) @ 2026-07-01 19:53 UTC (permalink / raw)
To: Thierry Reding
Cc: linaro-mm-sig, Lorenzo Stoakes, Sven Schnelle, Gerald Schaefer,
Thomas Zimmermann, Yury Norov, linux-media, linux-trace-kernel,
Rasmus Villemoes, Jonathan Hunter, linux-s390, Masami Hiramatsu,
Vlastimil Babka, Heiko Carstens, Mikko Perttunen,
David Hildenbrand, Steven Rostedt, iommu, linux-kernel,
Simona Vetter, Mike Rapoport, Maarten Lankhorst, Robin Murphy,
Russell King, dri-devel, Maxime Ripard, Marek Szyprowski,
Vasily Gorbik, Brian Starkey, Krzysztof Kozlowski,
Christian Borntraeger, John Stultz, Christian König,
Thierry Reding, devicetree, Benjamin Gaignard, Catalin Marinas,
linux-arm-kernel, Alexander Gordeev, Mathieu Desnoyers, linux-mm,
Sumit Semwal, Will Deacon, Thierry Reding, Luca Ceresoli,
Liam R. Howlett, Andrew Morton, Suren Baghdasaryan,
Sowjanya Komatineni, linux-tegra, Conor Dooley, Michal Hocko,
T.J. Mercier, David Airlie
In-Reply-To: <20260701-tegra-vpr-v3-2-d80f7b871bb4@nvidia.com>
On Wed, 01 Jul 2026 18:08:13 +0200, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> Add the memory-region and memory-region-names properties to the bindings
> for the display controllers and the host1x engine found on various Tegra
> generations. These memory regions are used to access firmware-provided
> framebuffer memory as well as the video protection region.
>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
> Changes in v3:
> - document properties for VIC
> ---
> .../devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml | 8 ++++++++
> .../devicetree/bindings/display/tegra/nvidia,tegra186-dc.yaml | 10 ++++++++++
> .../devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml | 10 +++++++++-
> .../bindings/display/tegra/nvidia,tegra20-host1x.yaml | 7 +++++++
> 4 files changed, 34 insertions(+), 1 deletion(-)
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml: properties:memory-region-names: 'anyOf' conditional failed, one must be fixed:
'maxitems' is not one of ['$ref', 'additionalItems', 'additionalProperties', 'allOf', 'anyOf', 'const', 'contains', 'default', 'dependencies', 'dependentRequired', 'dependentSchemas', 'deprecated', 'description', 'else', 'enum', 'exclusiveMaximum', 'exclusiveMinimum', 'items', 'if', 'minItems', 'minimum', 'maxItems', 'maximum', 'multipleOf', 'not', 'oneOf', 'pattern', 'patternProperties', 'properties', 'required', 'then', 'typeSize', 'unevaluatedProperties', 'uniqueItems']
'type' was expected
from schema $id: http://devicetree.org/meta-schemas/keywords.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml: properties:memory-region-names:items: {'enum': ['framebuffer', 'protected']} is not of type 'array'
from schema $id: http://devicetree.org/meta-schemas/string-array.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml: properties:memory-region-names: Additional properties are not allowed ('maxitems' was unexpected)
from schema $id: http://devicetree.org/meta-schemas/string-array.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml: properties:memory-region-names:items: {'enum': ['framebuffer', 'protected']} is not of type 'array'
from schema $id: http://devicetree.org/meta-schemas/string-array.yaml
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml: properties:memory-region-names: Additional properties are not allowed ('maxitems' was unexpected)
from schema $id: http://devicetree.org/meta-schemas/string-array.yaml
doc reference errors (make refcheckdocs):
See https://patchwork.kernel.org/project/devicetree/patch/20260701-tegra-vpr-v3-2-d80f7b871bb4@nvidia.com
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
^ permalink raw reply
* Re: [PATCH] tracing: Use more common error handling code in event_enable_trigger_parse()
From: Steven Rostedt @ 2026-07-01 20:37 UTC (permalink / raw)
To: Markus Elfring
Cc: linux-trace-kernel, Masami Hiramatsu, Mathieu Desnoyers,
Tom Zanussi, LKML, kernel-janitors
In-Reply-To: <bddf7714-0b80-4e94-9da8-55a263c270a5@web.de>
On Tue, 16 Jun 2026 22:33:17 +0200
Markus Elfring <Markus.Elfring@web.de> wrote:
> From: Markus Elfring <elfring@users.sourceforge.net>
> Date: Tue, 16 Jun 2026 22:22:52 +0200
>
> Use an additional label so that a bit of exception handling can be better
> reused at the end of this function implementation.
>
> This issue was detected by using the Coccinelle software.
>
> Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
> ---
> kernel/trace/trace_events_trigger.c | 10 ++++------
> 1 file changed, 4 insertions(+), 6 deletions(-)
>
> diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
> index 655db2e82513..7e17dd6d5b50 100644
> --- a/kernel/trace/trace_events_trigger.c
> +++ b/kernel/trace/trace_events_trigger.c
> @@ -1789,17 +1789,14 @@ int event_enable_trigger_parse(struct event_command *cmd_ops,
> enable_data->file = event_enable_file;
>
> trigger_data = trigger_data_alloc(cmd_ops, cmd, param, enable_data);
> - if (!trigger_data) {
> - kfree(enable_data);
> - return ret;
> - }
> + if (!trigger_data)
> + goto out_free_enable_data;
>
> if (remove) {
> event_trigger_unregister(cmd_ops, file, glob+1, trigger_data);
> kfree(trigger_data);
> - kfree(enable_data);
> ret = 0;
> - return ret;
> + goto out_free_enable_data;
> }
>
> /* Up the trigger_data count to make sure nothing frees it on failure */
> @@ -1837,6 +1834,7 @@ int event_enable_trigger_parse(struct event_command *cmd_ops,
> out_free:
> event_trigger_reset_filter(cmd_ops, trigger_data);
> event_trigger_free(trigger_data);
> +out_free_enable_data:
> kfree(enable_data);
Ug, let's not add more error labels now that we have cleanup code.
Untested, but this would be cleaner:
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 655db2e82513..7da096acaf2b 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -1739,7 +1739,7 @@ int event_enable_trigger_parse(struct event_command *cmd_ops,
char *glob, char *cmd, char *param_and_filter)
{
struct trace_event_file *event_enable_file;
- struct enable_trigger_data *enable_data;
+ struct enable_trigger_data *enable_data __free(kfree) = NULL;
struct event_trigger_data *trigger_data;
struct trace_array *tr = file->tr;
char *param, *filter;
@@ -1789,17 +1789,13 @@ int event_enable_trigger_parse(struct event_command *cmd_ops,
enable_data->file = event_enable_file;
trigger_data = trigger_data_alloc(cmd_ops, cmd, param, enable_data);
- if (!trigger_data) {
- kfree(enable_data);
+ if (!trigger_data)
return ret;
- }
if (remove) {
event_trigger_unregister(cmd_ops, file, glob+1, trigger_data);
kfree(trigger_data);
- kfree(enable_data);
- ret = 0;
- return ret;
+ return 0;
}
/* Up the trigger_data count to make sure nothing frees it on failure */
@@ -1829,6 +1825,7 @@ int event_enable_trigger_parse(struct event_command *cmd_ops,
goto out_disable;
event_trigger_free(trigger_data);
+ no_free_ptr(enable_free);
return ret;
out_disable:
trace_event_enable_disable(event_enable_file, 0, 1);
@@ -1837,7 +1834,6 @@ int event_enable_trigger_parse(struct event_command *cmd_ops,
out_free:
event_trigger_reset_filter(cmd_ops, trigger_data);
event_trigger_free(trigger_data);
- kfree(enable_data);
return ret;
}
-- Steve
^ permalink raw reply related
* [PATCH] tracing: Cleanup event_enable_trigger_parse() by using __free()
From: Steven Rostedt @ 2026-07-01 21:48 UTC (permalink / raw)
To: LKML, Linux Trace Kernel
Cc: Masami Hiramatsu, Mathieu Desnoyers, Markus Elfring
From: Steven Rostedt <rostedt@goodmis.org>
The enable_data variable gets freed on most error paths in
event_enable_trigger_parse(). Use free() to free it and just before
returning normally, call no_free_ptr(enable_data) to keep it from being
freed.
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
---
kernel/trace/trace_events_trigger.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 655db2e82513..fe5ca6ac7c8d 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -1739,7 +1739,7 @@ int event_enable_trigger_parse(struct event_command *cmd_ops,
char *glob, char *cmd, char *param_and_filter)
{
struct trace_event_file *event_enable_file;
- struct enable_trigger_data *enable_data;
+ struct enable_trigger_data *enable_data __free(kfree) = NULL;
struct event_trigger_data *trigger_data;
struct trace_array *tr = file->tr;
char *param, *filter;
@@ -1789,17 +1789,13 @@ int event_enable_trigger_parse(struct event_command *cmd_ops,
enable_data->file = event_enable_file;
trigger_data = trigger_data_alloc(cmd_ops, cmd, param, enable_data);
- if (!trigger_data) {
- kfree(enable_data);
+ if (!trigger_data)
return ret;
- }
if (remove) {
event_trigger_unregister(cmd_ops, file, glob+1, trigger_data);
kfree(trigger_data);
- kfree(enable_data);
- ret = 0;
- return ret;
+ return 0;
}
/* Up the trigger_data count to make sure nothing frees it on failure */
@@ -1829,6 +1825,7 @@ int event_enable_trigger_parse(struct event_command *cmd_ops,
goto out_disable;
event_trigger_free(trigger_data);
+ no_free_ptr(enable_data);
return ret;
out_disable:
trace_event_enable_disable(event_enable_file, 0, 1);
@@ -1837,7 +1834,6 @@ int event_enable_trigger_parse(struct event_command *cmd_ops,
out_free:
event_trigger_reset_filter(cmd_ops, trigger_data);
event_trigger_free(trigger_data);
- kfree(enable_data);
return ret;
}
--
2.53.0
^ permalink raw reply related
* Re: [RFC PATCH 2/4] tracing/probes: Compile all fetchargs into a single BPF program per event
From: Masami Hiramatsu @ 2026-07-01 22:40 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Steven Rostedt, Shuah Khan, Mathieu Desnoyers, linux-kernel,
linux-trace-kernel, linux-kselftest, bpf
In-Reply-To: <DJNGE5JG5ZY4.FEO5DZ21TL7Z@gmail.com>
On Wed, 01 Jul 2026 11:41:26 -0700
"Alexei Starovoitov" <alexei.starovoitov@gmail.com> wrote:
>
> Nack.
> I really don't like it.
> There were days in the past when the kernel generating bpf directly was appealing.
> These days are gone. Performance improvements for fetchargs is not a good reason
> to add all this complexity and bypass verifier checks.
> bpf insns should come from user space.
Thanks for your comment!
OK, I don't mind because this is a kind of investigation project. And some
people had asked me about the same idea, now I can tell them the result.
I'm satisfied with the current outcome, as this development process gave me
insight into the implementation of BPF and demonstrated the potential for
optimization via JIT. :)
And also, as noted in the cover letter, the current performance of fetcharg
is better than I thought, and is good enough for debugging. :)
BTW, I'm also interested in calling the verifier on this generated code.
Even it it is not merged, I think showing the correct way to implement it
will be useful in the future.
Thank you,
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply
* Re: [PATCH] tracing: Cleanup event_enable_trigger_parse() by using __free()
From: Masami Hiramatsu @ 2026-07-01 22:41 UTC (permalink / raw)
To: Steven Rostedt
Cc: LKML, Linux Trace Kernel, Masami Hiramatsu, Mathieu Desnoyers,
Markus Elfring
In-Reply-To: <20260701174829.67ab8a33@gandalf.local.home>
On Wed, 1 Jul 2026 17:48:29 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:
> From: Steven Rostedt <rostedt@goodmis.org>
>
> The enable_data variable gets freed on most error paths in
> event_enable_trigger_parse(). Use free() to free it and just before
> returning normally, call no_free_ptr(enable_data) to keep it from being
> freed.
>
This looks good to me.
Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Thank you,
> Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
> ---
> kernel/trace/trace_events_trigger.c | 12 ++++--------
> 1 file changed, 4 insertions(+), 8 deletions(-)
>
> diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
> index 655db2e82513..fe5ca6ac7c8d 100644
> --- a/kernel/trace/trace_events_trigger.c
> +++ b/kernel/trace/trace_events_trigger.c
> @@ -1739,7 +1739,7 @@ int event_enable_trigger_parse(struct event_command *cmd_ops,
> char *glob, char *cmd, char *param_and_filter)
> {
> struct trace_event_file *event_enable_file;
> - struct enable_trigger_data *enable_data;
> + struct enable_trigger_data *enable_data __free(kfree) = NULL;
> struct event_trigger_data *trigger_data;
> struct trace_array *tr = file->tr;
> char *param, *filter;
> @@ -1789,17 +1789,13 @@ int event_enable_trigger_parse(struct event_command *cmd_ops,
> enable_data->file = event_enable_file;
>
> trigger_data = trigger_data_alloc(cmd_ops, cmd, param, enable_data);
> - if (!trigger_data) {
> - kfree(enable_data);
> + if (!trigger_data)
> return ret;
> - }
>
> if (remove) {
> event_trigger_unregister(cmd_ops, file, glob+1, trigger_data);
> kfree(trigger_data);
> - kfree(enable_data);
> - ret = 0;
> - return ret;
> + return 0;
> }
>
> /* Up the trigger_data count to make sure nothing frees it on failure */
> @@ -1829,6 +1825,7 @@ int event_enable_trigger_parse(struct event_command *cmd_ops,
> goto out_disable;
>
> event_trigger_free(trigger_data);
> + no_free_ptr(enable_data);
> return ret;
> out_disable:
> trace_event_enable_disable(event_enable_file, 0, 1);
> @@ -1837,7 +1834,6 @@ int event_enable_trigger_parse(struct event_command *cmd_ops,
> out_free:
> event_trigger_reset_filter(cmd_ops, trigger_data);
> event_trigger_free(trigger_data);
> - kfree(enable_data);
>
> return ret;
> }
> --
> 2.53.0
>
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply
* Re: [PATCHv5 00/13] uprobes/x86: Fix red zone issue for optimized uprobes
From: Andrii Nakryiko @ 2026-07-01 23:13 UTC (permalink / raw)
To: Jiri Olsa
Cc: Oleg Nesterov, Peter Zijlstra, Ingo Molnar, Masami Hiramatsu,
Andrii Nakryiko, bpf, linux-trace-kernel
In-Reply-To: <20260701111337.53943-1-jolsa@kernel.org>
On Wed, Jul 1, 2026 at 4:13 AM Jiri Olsa <jolsa@kernel.org> wrote:
>
> hi,
> Andrii reported an issue with optimized uprobes [1] that can clobber
> redzone area with call instruction storing return address on stack
> where user code may keep temporary data without adjusting rsp.
>
> Fixing this by moving the optimized uprobes on top of 10-bytes nop
> instruction, so we can squeeze another instruction to escape the
> redzone area before doing the call.
>
> Note we need upstream update first for patch 3 (github.com/libbpf/usdt),
> if we decide to take this change.
>
> thanks,
> jirka
>
>
> v1: https://lore.kernel.org/bpf/20260514135342.22130-1-jolsa@kernel.org/
> v2: https://lore.kernel.org/bpf/20260518105957.123445-1-jolsa@kernel.org/
> v3: https://lore.kernel.org/bpf/20260521124411.31133-1-jolsa@kernel.org/
> v4: https://lore.kernel.org/bpf/20260526205840.173790-1-jolsa@kernel.org/
>
> v5 changes:
> - several selftests changes and reviewed-by tags [Jakub]
> - add more comments in int3_update_unoptimize [Andrii]
> - several other minor changes and acks [Oleg]
> - move insn_decode out of uprobe_init_insn to simplify the code
> - align uprobe_red_zone_test to 64 to make sure nop10 is not on page boundary
>
> v4 changes:
> - do not use 2nd int3 (ont +5 offset) because the call instruction
> is allways the same for the given nop10 address [Andrii/Peter]
> - unmap unused trampoline vma after unsuccesfull optimization [sashiko]
> - small change to patch#2 moved user_64bit_mode earlier in the path
> and pass/use mm_struct pointer directly from arch_uprobe_optimize
> instead of gettting current->mm
> Andrii, keeping your ack, please shout otherwise
>
> v3 changes:
> - use nop10 update suggested by Peter in [2]
> - remove struct uprobe_trampoline object, use vma objects directly instead
> - selftests fixes [sashiko]
> - ack from Andrii
>
> v2 changes:
> - several selftest fixes [sashiko]
> - consolidate is_lea_insn and is_call_insn insto single check [Jakub Sitnicki]
> - use proper mm_struct object in __in_uprobe_trampoline check [sashiko]
> - allow to copy uprobe trampolines vma objects on fork [sashiko]
> - change uprobe syscall detection error from -ENXIO to -EPROTO [Andrii]
> - added fork/clone tests
> - I kept the selftest changes and nop5->nop10 changes in separate
> commits for easier review, we can squash them later if we want to keep
> bisect working properly
>
>
> [1] https://lore.kernel.org/bpf/20260509003146.976844-1-andrii@kernel.org/
> [2] https://lore.kernel.org/bpf/20260518104306.GU3102624@noisy.programming.kicks-ass.net/#t
> ---
ASAN-enabled test_progs runs are not happy in CI, can you please check?
> Andrii Nakryiko (1):
> selftests/bpf: Add tests for uprobe nop10 red zone clobbering
>
> Jiri Olsa (12):
> uprobes/x86: Use proper mm_struct in __in_uprobe_trampoline
> uprobes/x86: Remove struct uprobe_trampoline object
> uprobes/x86: Do not leak trampoline vma mapping on optimization failure
> uprobes/x86: Allow to copy uprobe trampolines on fork
> uprobes/x86: Move optimized uprobe from nop5 to nop10
> libbpf: Change has_nop_combo to work on top of nop10
> libbpf: Detect uprobe syscall with new error
> selftests/bpf: Emit nop,nop10 instructions combo for x86_64 arch
> selftests/bpf: Change uprobe syscall tests to use nop10
> selftests/bpf: Change uprobe/usdt trigger bench code to use nop10
> selftests/bpf: Add reattach tests for uprobe syscall
> selftests/bpf: Add tests for forked/cloned optimized uprobes
>
> arch/x86/kernel/uprobes.c | 416 +++++++++++++++++++++++++++++++++++++++++++-----------------------------
> include/linux/uprobes.h | 5 -
> kernel/events/uprobes.c | 10 --
> kernel/fork.c | 1 -
> tools/lib/bpf/features.c | 4 +-
> tools/lib/bpf/usdt.c | 16 +--
> tools/testing/selftests/bpf/bench.c | 20 ++--
> tools/testing/selftests/bpf/benchs/bench_trigger.c | 38 +++----
> tools/testing/selftests/bpf/benchs/run_bench_uprobes.sh | 2 +-
> tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c | 326 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
> tools/testing/selftests/bpf/prog_tests/usdt.c | 74 +++++++++++--
> tools/testing/selftests/bpf/progs/test_usdt.c | 25 +++++
> tools/testing/selftests/bpf/usdt.h | 2 +-
> tools/testing/selftests/bpf/usdt_2.c | 15 ++-
> 14 files changed, 698 insertions(+), 256 deletions(-)
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox