[PATCH v2 01/14] iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap
Andrew Jones <[email protected]> Fri, 24 Jul 2026 17:12:05 +0200
| Newsgroups | dev.linux.lists.iommu,org.infradead.lists.linux-riscv,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
struct iommufd_sw_msi_maps currently uses a fixed 64-bit bitmap, capping the number of distinct SW MSI mappings a context or hwpt can track. An upcoming caller needs one mapping per possible CPU. Convert the fixed bitmap to a pointer plus size that grows on demand via iommufd_sw_msi_maps_ensure(). Add iommufd_sw_msi_maps_test_bit() alongside it: unlike the __set_bit() call sites, which always follow their own successful iommufd_sw_msi_maps_ensure() call on the same id, iommufd_group_setup_msi() tests an id from the fd-global sw_msi_list against a specific group's required_sw_msi map, which may not have been grown to cover that id yet. Signed-off-by: Andrew Jones <[email protected]> --- drivers/iommu/iommufd/device.c | 3 +- drivers/iommu/iommufd/driver.c | 38 ++++++++++++++++--------- drivers/iommu/iommufd/hw_pagetable.c | 1 + drivers/iommu/iommufd/iommufd_private.h | 38 +++++++++++++++++++++++-- 4 files changed, 64 insertions(+), 16 deletions(-) diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 170a7005f0bc..402251c7b887 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..3cd617dfc351 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 == UINT_MAX || + max_pgoff > (ULONG_MAX - sw_msi_start) / PAGE_SIZE) return ERR_PTR(-EOVERFLOW); cur = kzalloc_obj(*cur); @@ -222,21 +224,26 @@ 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 +297,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..44d6dc94fb6b 100644 --- a/drivers/iommu/iommufd/iommufd_private.h +++ b/drivers/iommu/iommufd/iommufd_private.h @@ -29,11 +29,45 @@ 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; }; +/* 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; + + new_nbits = ALIGN(max(maps->nbits ? maps->nbits * 2 : 64U, id + 1), + BITS_PER_LONG); + if (new_nbits <= id) + return -EOVERFLOW; + 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, -- 2.43.0