[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