Re: [PATCH] dma-iommu: Introduce API to reserve IOVA regions for dynamically created devices

From: Vishnu Reddy

Date: Wed Jun 10 2026 - 10:34:50 EST



On 1/19/2026 7:43 PM, Robin Murphy wrote:
> On 19/01/2026 5:49 am, Vishnu Reddy wrote:
>> Drivers can create child devices dynamically. These child devices often
>> inherit parent device tree node, including its reserved iommu-address
>> ranges.
>
> Which frankly sounds entirely wrong. If there are distinct devices with
> distinct hardware properties, then they should be properly described as such,
> and handled by a proper bus driver. And if different devices have different
> DMA restrictions, that should be described by "dma-ranges", not by abusing
> reserved regions.
>

Hi Robin,

The VPU is a single hardware unit, but internally it consists of
multiple functional blocks. Each block has its own distinct Stream ID
(SID) and operates with a different IOVA range requirement:

  +--------------------------------------------------+
  |                  VPU Hardware                    |
  |                                                  |
  |  +------------+   SID-0   IOVA: 600MB - 3500MB   |
  |  |  Block 0   |                                  |
  |  +------------+                                  |
  |                                                  |
  |  +------------+   SID-1   IOVA: 0MB  - 3500MB    |
  |  |  Block 1   |                                  |
  |  +------------+                                  |
  |                                                  |
  |  +------------+   SID-2   IOVA: 16MB - 600MB     |
  |  |  Block 2   |                                  |
  |  +------------+                                  |
  +--------------------------------------------------+

Each Stream ID maps to a distinct IOMMU context bank, and each context
bank enforces a different IOVA range.

The upper boundary can be enforced via dma_set_mask_and_coherent. However,
enforcing the lower boundary (e.g., blocking 0 to 600MB for Block 0,
and 0 to 16MB for Block 2) requires explicit IOVA reservation,
which is what dma_iova_set_resv_region() provides in this patch.

These are synthetic child devices created at runtime and do not have their
own of_node. Inheriting the parent of_node might not be the correct way.

A crash was observed on one of the platforms due to the absence of
reserved regions. This API would help avoid such crashes by allowing
drivers to explicitly reserve the restricted IOVA range.
https://gitlab.freedesktop.org/drm/msm/-/work_items/100

Thanks,
Vishnu Reddy.

> Thanks,
> Robin.
>
>> While device tree can declare the multiple iommu-address ranges,
>> they still apply uniformly to any child inheriting the parent OF node.
>>
>> This creates a limitation when child requires a different iommu-address
>> range than its parent, as device tree cannot express the reservation per
>> child.
>>
>> Example layout and use case:
>> ----------------------------
>> A multimedia subsystem with multiple child devices and distinct ranges:
>>
>>      +-----------------------------------------------------------+
>>      | Device A reserved region (256 MB)                         |
>>      | 0x00000000 - 0x10000000                                   |
>>      +-----------------------------------------------------------+
>>      | Device B reserved region (512 MB)                         |
>>      | 0x00000000 - 0x20000000                                   |
>>      +-----------------------------------------------------------+
>>      | Device C reserved region (600 MB)                         |
>>      | 0x00000000 - 0x25800000                                   |
>>      +-----------------------------------------------------------+
>>      | Other reserved regions                                    |
>>      +-----------------------------------------------------------+
>>
>> If we add Device C's reserved range (0x0 to 0x025800000) into iommu
>> address range into device tree and assign that OF node to Device B
>> and C, it wastes memory:
>> - Device A wastes ~344 MB
>> - Device B wastes ~88 MB
>>
>> To avoid this, drivers can use the introduced API to set their device
>> specific IOVA region at runtime, ensuring the DMA mapping without
>> unnecessary address space reservation.
>>
>> Signed-off-by: Vishnu Reddy <busanna.reddy@xxxxxxxxxxxxxxxx>
>> Link:
>> https://lore.kernel.org/all/20251220023555.3017456-1-busanna.reddy@xxxxxxxxxxxxxxxx/
>> ---
>>   drivers/iommu/dma-iommu.c   | 44 +++++++++++++++++++++++++++++++++++++
>>   include/linux/dma-mapping.h |  7 ++++++
>>   2 files changed, 51 insertions(+)
>>
>> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
>> index aeaf8fad985c3..baee119e1c277 100644
>> --- a/drivers/iommu/dma-iommu.c
>> +++ b/drivers/iommu/dma-iommu.c
>> @@ -1743,6 +1743,50 @@ size_t iommu_dma_max_mapping_size(struct device *dev)
>>       return SIZE_MAX;
>>   }
>>   +/**
>> + * dma_iova_set_resv_region - Set a reserved region in the IOVA space
>> + * @dev: Device to set the reserved region for
>> + * @start: Start of the reserved region
>> + * @length: Length of the reserved region
>> + *
>> + * This function enables drivers to set device specific reservations at
>> + * runtime, which is particularly useful for dynamically created child
>> + * devices that requires distinct IOVA ranges than parent.
>> + *
>> + * Returns:
>> + *  0        - Success
>> + *  -ENODEV  - No valid IOMMU DMA domain for the device
>> + *  -EINVAL  - Invalid domain or cookie type for this operation
>> + *  -ENOMEM  - Failed to reserve the requested range
>> + */
>> +int dma_iova_set_resv_region(struct device *dev, unsigned long start,
>> +                 unsigned long length)
>> +{
>> +    struct iommu_dma_cookie *cookie;
>> +    struct iommu_domain *domain;
>> +    struct iova_domain *iovad;
>> +    unsigned long lo, hi;
>> +
>> +    domain = iommu_get_dma_domain(dev);
>> +    if (!domain || domain->type != IOMMU_DOMAIN_DMA)
>> +        return -ENODEV;
>> +
>> +    cookie = domain->iova_cookie;
>> +    if (!cookie || domain->cookie_type != IOMMU_COOKIE_DMA_IOVA)
>> +        return -EINVAL;
>> +
>> +    iovad = &cookie->iovad;
>> +
>> +    lo = iova_pfn(iovad, start);
>> +    hi = iova_pfn(iovad, start + length - 1);
>> +
>> +    if (!reserve_iova(iovad, lo, hi))
>> +        return -ENOMEM;
>> +
>> +    return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(dma_iova_set_resv_region);
>> +
>>   /**
>>    * dma_iova_try_alloc - Try to allocate an IOVA space
>>    * @dev: Device to allocate the IOVA space for
>> diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
>> index 29973baa05816..75b65160a37f3 100644
>> --- a/include/linux/dma-mapping.h
>> +++ b/include/linux/dma-mapping.h
>> @@ -347,6 +347,8 @@ static inline bool dma_use_iova(struct dma_iova_state
>> *state)
>>       return state->__size != 0;
>>   }
>>   +int dma_iova_set_resv_region(struct device *dev, unsigned long start,
>> +                 unsigned long length);
>>   bool dma_iova_try_alloc(struct device *dev, struct dma_iova_state *state,
>>           phys_addr_t phys, size_t size);
>>   void dma_iova_free(struct device *dev, struct dma_iova_state *state);
>> @@ -366,6 +368,11 @@ static inline bool dma_use_iova(struct dma_iova_state
>> *state)
>>   {
>>       return false;
>>   }
>> +static inline int dma_iova_set_resv_region(struct device *dev, unsigned long
>> start,
>> +                       unsigned long length)
>> +{
>> +    return -ENODEV;
>> +}
>>   static inline bool dma_iova_try_alloc(struct device *dev,
>>           struct dma_iova_state *state, phys_addr_t phys, size_t size)
>>   {
>