[PATCH v5 02/17] iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap

Nutty.Liu nutty.liu at hotmail.com
Tue Sep 1 00:52:39 PDT 2026


On 8/31/2026 10:59 PM, Andrew Jones wrote:
> struct iommufd_sw_msi_maps uses a fixed 64-bit bitmap, limiting each
> group and hardware page table to 64 software MSI mappings. RISC-V
> interrupt remapping needs a mapping for every possible CPU, so this
> limit is insufficient.
>
> Make the bitmap grow on demand and treat IDs beyond its current size
> as absent. Cap it at 16K entries to bound allocation size while leaving
> ample room for expected software MSI users.
>
> Signed-off-by: Andrew Jones <andrew.jones at oss.qualcomm.com>
Reviewed-by: Nutty Liu <nutty.liu at hotmail.com>

Thanks,
Nutty
> ---
>   drivers/iommu/iommufd/device.c          |  3 +-
>   drivers/iommu/iommufd/driver.c          | 37 ++++++++++++++--------
>   drivers/iommu/iommufd/hw_pagetable.c    |  1 +
>   drivers/iommu/iommufd/iommufd_private.h | 41 +++++++++++++++++++++++--
>   4 files changed, 66 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c
> index d488c23fd353..868f1e591208 100644
> --- a/drivers/iommu/iommufd/device.c
> +++ b/drivers/iommu/iommufd/device.c
> @@ -34,6 +34,7 @@ static void iommufd_group_release(struct kref *kref)
>   		   NULL, GFP_KERNEL);
>   	iommu_group_put(igroup->group);
>   	mutex_destroy(&igroup->lock);
> +	kfree(igroup->required_sw_msi.bitmap);
>   	kfree(igroup);
>   }
>   
> @@ -384,7 +385,7 @@ static int iommufd_group_setup_msi(struct iommufd_group *igroup,
>   		int rc;
>   
>   		if (cur->sw_msi_start != igroup->sw_msi_start ||
> -		    !test_bit(cur->id, igroup->required_sw_msi.bitmap))
> +		    !iommufd_sw_msi_maps_test_bit(&igroup->required_sw_msi, cur->id))
>   			continue;
>   
>   		rc = iommufd_sw_msi_install(ictx, hwpt_paging, cur);
> diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c
> index 3b8067976eac..d0b05b1fda42 100644
> --- a/drivers/iommu/iommufd/driver.c
> +++ b/drivers/iommu/iommufd/driver.c
> @@ -196,13 +196,15 @@ iommufd_sw_msi_get_map(struct iommufd_ctx *ictx, phys_addr_t msi_addr,
>   	list_for_each_entry(cur, &ictx->sw_msi_list, sw_msi_item) {
>   		if (cur->sw_msi_start != sw_msi_start)
>   			continue;
> +		if (cur->pgoff == UINT_MAX)
> +			return ERR_PTR(-EOVERFLOW);
>   		max_pgoff = max(max_pgoff, cur->pgoff + 1);
>   		if (cur->msi_addr == msi_addr)
>   			return cur;
>   	}
>   
> -	if (ictx->sw_msi_id >=
> -	    BITS_PER_BYTE * sizeof_field(struct iommufd_sw_msi_maps, bitmap))
> +	if (ictx->sw_msi_id > IOMMUFD_SW_MSI_MAX_ID ||
> +	    max_pgoff > (ULONG_MAX - sw_msi_start) / PAGE_SIZE)
>   		return ERR_PTR(-EOVERFLOW);
>   
>   	cur = kzalloc_obj(*cur);
> @@ -222,21 +224,25 @@ int iommufd_sw_msi_install(struct iommufd_ctx *ictx,
>   			   struct iommufd_sw_msi_map *msi_map)
>   {
>   	unsigned long iova;
> +	int rc;
>   
>   	lockdep_assert_held(&ictx->sw_msi_lock);
>   
> +	if (iommufd_sw_msi_maps_test_bit(&hwpt_paging->present_sw_msi, msi_map->id))
> +		return 0;
> +
>   	iova = msi_map->sw_msi_start + msi_map->pgoff * PAGE_SIZE;
> -	if (!test_bit(msi_map->id, hwpt_paging->present_sw_msi.bitmap)) {
> -		int rc;
> -
> -		rc = iommu_map(hwpt_paging->common.domain, iova,
> -			       msi_map->msi_addr, PAGE_SIZE,
> -			       IOMMU_WRITE | IOMMU_READ | IOMMU_MMIO,
> -			       GFP_KERNEL_ACCOUNT);
> -		if (rc)
> -			return rc;
> -		__set_bit(msi_map->id, hwpt_paging->present_sw_msi.bitmap);
> -	}
> +	rc = iommufd_sw_msi_maps_ensure(&hwpt_paging->present_sw_msi, msi_map->id);
> +	if (rc)
> +		return rc;
> +
> +	rc = iommu_map(hwpt_paging->common.domain, iova,
> +		       msi_map->msi_addr, PAGE_SIZE,
> +		       IOMMU_WRITE | IOMMU_READ | IOMMU_MMIO,
> +		       GFP_KERNEL_ACCOUNT);
> +	if (rc)
> +		return rc;
> +	__set_bit(msi_map->id, hwpt_paging->present_sw_msi.bitmap);
>   	return 0;
>   }
>   EXPORT_SYMBOL_NS_GPL(iommufd_sw_msi_install, "IOMMUFD_INTERNAL");
> @@ -290,6 +296,11 @@ int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
>   	if (IS_ERR(msi_map))
>   		return PTR_ERR(msi_map);
>   
> +	rc = iommufd_sw_msi_maps_ensure(&handle->idev->igroup->required_sw_msi,
> +					msi_map->id);
> +	if (rc)
> +		return rc;
> +
>   	rc = iommufd_sw_msi_install(ictx, hwpt_paging, msi_map);
>   	if (rc)
>   		return rc;
> diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c
> index 623cc608ca0c..54873de43eb0 100644
> --- a/drivers/iommu/iommufd/hw_pagetable.c
> +++ b/drivers/iommu/iommufd/hw_pagetable.c
> @@ -32,6 +32,7 @@ void iommufd_hwpt_paging_destroy(struct iommufd_object *obj)
>   	}
>   
>   	__iommufd_hwpt_destroy(&hwpt_paging->common);
> +	kfree(hwpt_paging->present_sw_msi.bitmap);
>   	refcount_dec(&hwpt_paging->ioas->obj.users);
>   }
>   
> diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
> index 43fbc5bed8de..9ca5f9f92cdf 100644
> --- a/drivers/iommu/iommufd/iommufd_private.h
> +++ b/drivers/iommu/iommufd/iommufd_private.h
> @@ -9,6 +9,7 @@
>   #include <linux/iova_bitmap.h>
>   #include <linux/maple_tree.h>
>   #include <linux/rwsem.h>
> +#include <linux/slab.h>
>   #include <linux/uaccess.h>
>   #include <linux/xarray.h>
>   #include <uapi/linux/iommufd.h>
> @@ -29,11 +30,47 @@ struct iommufd_sw_msi_map {
>   	unsigned int id;
>   };
>   
> -/* Bitmap of struct iommufd_sw_msi_map::id */
> +/* Bitmap of struct iommufd_sw_msi_map::id; starts empty, grows on demand. */
>   struct iommufd_sw_msi_maps {
> -	DECLARE_BITMAP(bitmap, 64);
> +	unsigned long *bitmap;
> +	unsigned int nbits;
>   };
>   
> +/* Large enough for foreseeable SW MSI users while bounding bitmap growth. */
> +#define IOMMUFD_SW_MSI_MAX_ID	(16U * 1024 - 1)
> +
> +/* Grow bitmap to accommodate id. Must be called under ictx->sw_msi_lock. */
> +static inline int iommufd_sw_msi_maps_ensure(struct iommufd_sw_msi_maps *maps,
> +					     unsigned int id)
> +{
> +	unsigned long *new_bitmap;
> +	unsigned int new_nbits;
> +
> +	if (id < maps->nbits)
> +		return 0;
> +	if (id > IOMMUFD_SW_MSI_MAX_ID)
> +		return -EOVERFLOW;
> +
> +	new_nbits = max(ALIGN(id + 1, BITS_PER_LONG), 64U);
> +	new_bitmap = krealloc(maps->bitmap,
> +			      BITS_TO_LONGS(new_nbits) * sizeof(unsigned long),
> +			      GFP_KERNEL_ACCOUNT);
> +	if (!new_bitmap)
> +		return -ENOMEM;
> +	bitmap_clear(new_bitmap, maps->nbits, new_nbits - maps->nbits);
> +	maps->bitmap = new_bitmap;
> +	maps->nbits = new_nbits;
> +	return 0;
> +}
> +
> +static inline bool iommufd_sw_msi_maps_test_bit(const struct iommufd_sw_msi_maps *maps,
> +						unsigned int id)
> +{
> +	if (id >= maps->nbits)
> +		return false;
> +	return test_bit(id, maps->bitmap);
> +}
> +
>   #ifdef CONFIG_IRQ_MSI_IOMMU
>   int iommufd_sw_msi_install(struct iommufd_ctx *ictx,
>   			   struct iommufd_hwpt_paging *hwpt_paging,



More information about the linux-riscv mailing list