[PATCH RFC v2 2/5] of: get dma area lower limit

2024-04-08 Thread Baruch Siach
of_dma_get_max_cpu_address() returns the highest CPU address that
devices can use for DMA. The implicit assumption is that all CPU
addresses below that limit are suitable for DMA. However the
'dma-ranges' property this code uses also encodes a lower limit for DMA
that is potentially non zero.

Rename to of_dma_get_cpu_limits(), and extend to retrieve also the lower
limit for the same 'dma-ranges' property describing the high limit.

Update callers of of_dma_get_max_cpu_address(). No functional change
intended.

Signed-off-by: Baruch Siach 
---
 arch/arm64/mm/init.c  |  2 +-
 drivers/of/address.c  | 38 +++---
 drivers/of/unittest.c |  8 
 include/linux/of.h| 11 ---
 4 files changed, 40 insertions(+), 19 deletions(-)

diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index 00508c69ca9e..77e942ca578b 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -128,7 +128,7 @@ static void __init zone_sizes_init(void)
 
 #ifdef CONFIG_ZONE_DMA
acpi_zone_dma_limit = acpi_iort_dma_get_max_cpu_address();
-   dt_zone_dma_limit = of_dma_get_max_cpu_address(NULL);
+   of_dma_get_cpu_limits(NULL, &dt_zone_dma_limit, NULL);
zone_dma_limit = min(dt_zone_dma_limit, acpi_zone_dma_limit);
arm64_dma_phys_limit = max_zone_phys(zone_dma_limit);
max_zone_pfns[ZONE_DMA] = PFN_DOWN(arm64_dma_phys_limit);
diff --git a/drivers/of/address.c b/drivers/of/address.c
index ae46a3605904..ac009b3bb63b 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -964,21 +964,25 @@ int of_dma_get_range(struct device_node *np, const struct 
bus_dma_region **map)
 #endif /* CONFIG_HAS_DMA */
 
 /**
- * of_dma_get_max_cpu_address - Gets highest CPU address suitable for DMA
+ * of_dma_get_cpu_limits - Gets highest CPU address suitable for DMA
  * @np: The node to start searching from or NULL to start from the root
+ * @max: Pointer to high address limit or NULL if not needed
+ * @min: Pointer to low address limit or NULL if not needed
  *
  * Gets the highest CPU physical address that is addressable by all DMA masters
- * in the sub-tree pointed by np, or the whole tree if NULL is passed. If no
- * DMA constrained device is found, it returns PHYS_ADDR_MAX.
+ * in the sub-tree pointed by np, or the whole tree if @np in NULL. If no
+ * DMA constrained device is found, @*max is PHYS_ADDR_MAX, and @*low is 0.
  */
-phys_addr_t __init of_dma_get_max_cpu_address(struct device_node *np)
+void __init of_dma_get_cpu_limits(struct device_node *np,
+   phys_addr_t *max, phys_addr_t *min)
 {
phys_addr_t max_cpu_addr = PHYS_ADDR_MAX;
struct of_range_parser parser;
-   phys_addr_t subtree_max_addr;
+   phys_addr_t min_cpu_addr = 0;
struct device_node *child;
struct of_range range;
const __be32 *ranges;
+   u64 cpu_start = 0;
u64 cpu_end = 0;
int len;
 
@@ -988,21 +992,33 @@ phys_addr_t __init of_dma_get_max_cpu_address(struct 
device_node *np)
ranges = of_get_property(np, "dma-ranges", &len);
if (ranges && len) {
of_dma_range_parser_init(&parser, np);
-   for_each_of_range(&parser, &range)
-   if (range.cpu_addr + range.size > cpu_end)
+   for_each_of_range(&parser, &range) {
+   if (range.cpu_addr + range.size > cpu_end) {
cpu_end = range.cpu_addr + range.size - 1;
+   cpu_start = range.cpu_addr;
+   }
+   }
 
-   if (max_cpu_addr > cpu_end)
+   if (max_cpu_addr > cpu_end) {
max_cpu_addr = cpu_end;
+   min_cpu_addr = cpu_start;
+   }
}
 
for_each_available_child_of_node(np, child) {
-   subtree_max_addr = of_dma_get_max_cpu_address(child);
-   if (max_cpu_addr > subtree_max_addr)
+   phys_addr_t subtree_max_addr, subtree_min_addr;
+
+   of_dma_get_cpu_limits(child, &subtree_max_addr, 
&subtree_min_addr);
+   if (max_cpu_addr > subtree_max_addr) {
max_cpu_addr = subtree_max_addr;
+   min_cpu_addr = subtree_min_addr;
+   }
}
 
-   return max_cpu_addr;
+   if (max)
+   *max = max_cpu_addr;
+   if (min)
+   *min = min_cpu_addr;
 }
 
 /**
diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c
index 6b5c36b6a758..2d632d4ec5b1 100644
--- a/drivers/of/unittest.c
+++ b/drivers/of/unittest.c
@@ -921,7 +921,7 @@ static void __init of_unittest_changeset(void)
 #endif
 }
 
-static void __init of_unittest_dma_get_max_cpu_address(void)
+static void __init of_unittest_dma_get_cpu_limits(void)
 {
struct device_node *np;
phys_addr_t cpu_addr;
@@ -935,9 +935,9 @@ static void __init of_unittest_dma_get_max_cpu_address(void)
  

Re: [PATCH RFC v2 2/5] of: get dma area lower limit

2024-06-18 Thread Catalin Marinas
On Tue, Apr 09, 2024 at 09:17:55AM +0300, Baruch Siach wrote:
> of_dma_get_max_cpu_address() returns the highest CPU address that
> devices can use for DMA. The implicit assumption is that all CPU
> addresses below that limit are suitable for DMA. However the
> 'dma-ranges' property this code uses also encodes a lower limit for DMA
> that is potentially non zero.
> 
> Rename to of_dma_get_cpu_limits(), and extend to retrieve also the lower
> limit for the same 'dma-ranges' property describing the high limit.

I don't understand the reason for the lower limit. The way the Linux
zones work is that ZONE_DMA always starts from the start of the RAM. It
doesn't matter whether it's 0 or not, you'd not allocate below the start
of RAM anyway. If you have a device that cannot use the bottom of the
RAM, it is pretty broken and not supported by Linux.

I think you added this limit before we tried to move away from
zone_dma_bits to a non-power-of-two limit (zone_dma_limit). With the
latter, we no longer need tricks with the lower limit,
of_dma_get_max_cpu_address() should capture the smallest upper CPU
address limit supported by all devices (and that's where ZONE_DMA should
end).

-- 
Catalin


Re: [PATCH RFC v2 2/5] of: get dma area lower limit

2024-07-25 Thread Baruch Siach
Hi Catalin,

On Tue, Jun 18 2024, Catalin Marinas wrote:
> On Tue, Apr 09, 2024 at 09:17:55AM +0300, Baruch Siach wrote:
>> of_dma_get_max_cpu_address() returns the highest CPU address that
>> devices can use for DMA. The implicit assumption is that all CPU
>> addresses below that limit are suitable for DMA. However the
>> 'dma-ranges' property this code uses also encodes a lower limit for DMA
>> that is potentially non zero.
>> 
>> Rename to of_dma_get_cpu_limits(), and extend to retrieve also the lower
>> limit for the same 'dma-ranges' property describing the high limit.
>
> I don't understand the reason for the lower limit. The way the Linux
> zones work is that ZONE_DMA always starts from the start of the RAM. It
> doesn't matter whether it's 0 or not, you'd not allocate below the start
> of RAM anyway. If you have a device that cannot use the bottom of the
> RAM, it is pretty broken and not supported by Linux.

I won't argue with that assertion. My target system RAM happens to start
at that the lower end of devices DMA zone, so I'm fine with skipping
this patch.

Just curious. What is the inherent limitation that prevents Linux from
supporting DMA zone with lower limit above RAM start?

Thanks,
baruch

-- 
 ~. .~   Tk Open Systems
=}ooO--U--Ooo{=
   - bar...@tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -


Re: [PATCH RFC v2 2/5] of: get dma area lower limit

2024-07-30 Thread Catalin Marinas
On Thu, Jul 25, 2024 at 02:49:01PM +0300, Baruch Siach wrote:
> Hi Catalin,
> 
> On Tue, Jun 18 2024, Catalin Marinas wrote:
> > On Tue, Apr 09, 2024 at 09:17:55AM +0300, Baruch Siach wrote:
> >> of_dma_get_max_cpu_address() returns the highest CPU address that
> >> devices can use for DMA. The implicit assumption is that all CPU
> >> addresses below that limit are suitable for DMA. However the
> >> 'dma-ranges' property this code uses also encodes a lower limit for DMA
> >> that is potentially non zero.
> >> 
> >> Rename to of_dma_get_cpu_limits(), and extend to retrieve also the lower
> >> limit for the same 'dma-ranges' property describing the high limit.
> >
> > I don't understand the reason for the lower limit. The way the Linux
> > zones work is that ZONE_DMA always starts from the start of the RAM. It
> > doesn't matter whether it's 0 or not, you'd not allocate below the start
> > of RAM anyway. If you have a device that cannot use the bottom of the
> > RAM, it is pretty broken and not supported by Linux.
> 
> I won't argue with that assertion. My target system RAM happens to start
> at that the lower end of devices DMA zone, so I'm fine with skipping
> this patch.
> 
> Just curious. What is the inherent limitation that prevents Linux from
> supporting DMA zone with lower limit above RAM start?

It's the way the zone allocation fallback mechanism works. Let's say a
ZONE_DMA32 allocation fails, it falls back to ZONE_DMA and it's supposed
to be compatible with the GFP_DMA32 request. If you have some other zone
below ZONE_DMA, it should also be compatible with GFP_DMA allocations.

-- 
Catalin