Skip to content

iommufd: Add vIOMMU infrastructure (Part-4 HW QUEUE)

Nicolin Chen linux-arm-kernel linux-doc linux-iommu linux-kselftest linux-patches linux-tegra lkml 9 versions – In review

Get series with b4
b4 am [email protected]

Versions newest first · linked by author and title

  1. v9 Latest29 patches 12 repliesIn review
  2. v829 patches 11 repliesSuperseded
  3. v728 patches 38 repliesSuperseded
Earlier versions · 6
VersionPostedPatchesRepliesStatus
v625 patches51 repliesSuperseded
v529 patches49 repliesSuperseded
v423 patches81 repliesSuperseded
v323 patches17 repliesSuperseded
v222 patches123 repliesSuperseded
v116 patches35 repliesSuperseded

Patches in v9 No applies recorded yet

#Subject and filesSizeRepliesApplied
1/29iommufd: Report unmapped bytes in the error path of iopt_unmap_iova_rangedrivers/iommu/iommufd/io_pagetable.c+5 -20 replies
2/29iommufd: Correct virt_id kdoc at struct iommu_vdevice_allocinclude/uapi/linux/iommufd.h+1 -10 replies
3/29iommufd/viommu: Explicitly define vdev->virt_iddrivers/iommu/iommufd/driver.c drivers/iommu/iommufd/iommufd_private.h drivers/iommu/iommufd/viommu.c+9 -40 replies
4/29iommu: Use enum iommu_hw_info_type for type in hw_info opdrivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h drivers/iommu/intel/iommu.c +2 more+11 -70 replies
5/29iommu: Add iommu_copy_struct_to_user helperinclude/linux/iommu.h+40 -00 replies
6/29iommu: Pass in a driver-level user data structure to viommu_init opdrivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h drivers/iommu/iommufd/selftest.c +2 more+9 -50 replies
7/29iommufd/viommu: Allow driver-specific user data for a vIOMMU objectdrivers/iommu/iommufd/viommu.c include/uapi/linux/iommufd.h+13 -10 replies
8/29iommufd/selftest: Support user_data in mock_viommu_allocdrivers/iommu/iommufd/iommufd_test.h drivers/iommu/iommufd/selftest.c+28 -00 replies
9/29iommufd/selftest: Add coverage for viommu datatools/testing/selftests/iommu/iommufd.c tools/testing/selftests/iommu/iommufd_fail_nth.c tools/testing/selftests/iommu/iommufd_utils.h+40 -170 replies
10/29iommufd/access: Add internal APIs for HW queue to usedrivers/iommu/iommufd/device.c drivers/iommu/iommufd/iommufd_private.h+73 -90 replies
11/29iommufd/access: Bypass access->ops->unmap for internal usedrivers/iommu/iommufd/device.c drivers/iommu/iommufd/io_pagetable.c drivers/iommu/iommufd/io_pagetable.h +1 more+31 -100 replies
12/29iommufd/viommu: Add driver-defined vDEVICE supportdrivers/iommu/iommufd/iommufd_private.h drivers/iommu/iommufd/viommu.c include/linux/iommufd.h+56 -130 replies
13/29iommufd/viommu: Introduce IOMMUFD_OBJ_HW_QUEUE and its related structinclude/linux/iommufd.h include/uapi/linux/iommufd.h+51 -00 replies
14/29iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctldrivers/iommu/iommufd/iommufd_private.h drivers/iommu/iommufd/main.c drivers/iommu/iommufd/viommu.c +2 more+222 -00 replies
15/29iommufd/driver: Add iommufd_hw_queue_depend/undepend() helpersdrivers/iommu/iommufd/driver.c include/linux/iommufd.h+72 -00 replies
16/29iommufd/selftest: Add coverage for IOMMUFD_CMD_HW_QUEUE_ALLOCdrivers/iommu/iommufd/iommufd_test.h drivers/iommu/iommufd/selftest.c tools/testing/selftests/iommu/iommufd.c +2 more+196 -01 reply
17/29iommufd: Add mmap interfacedrivers/iommu/iommufd/driver.c drivers/iommu/iommufd/iommufd_private.h drivers/iommu/iommufd/main.c +1 more+171 -02 replies
18/29iommufd/selftest: Add coverage for the new mmap interfacedrivers/iommu/iommufd/iommufd_test.h drivers/iommu/iommufd/selftest.c tools/testing/selftests/iommu/iommufd.c +1 more+59 -11 reply
19/29Documentation: userspace-api: iommufd: Update HW QUEUEDocumentation/userspace-api/iommufd.rst+12 -00 replies
20/29iommu: Allow an input type in hw_info opdrivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c drivers/iommu/intel/iommu.c drivers/iommu/iommufd/device.c +3 more+20 -20 replies
21/29iommufd: Allow an input data_type via iommu_hw_infodrivers/iommu/iommufd/device.c include/uapi/linux/iommufd.h+25 -40 replies
22/29iommufd/selftest: Update hw_info coverage for an input data_typetools/testing/selftests/iommu/iommufd.c tools/testing/selftests/iommu/iommufd_fail_nth.c tools/testing/selftests/iommu/iommufd_utils.h+46 -235 replies
23/29iommu/arm-smmu-v3-iommufd: Add vsmmu_size/type and vsmmu_init impl opsdrivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h+13 -03 replies
24/29iommu/arm-smmu-v3-iommufd: Add hw_info to impl_opsdrivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h+13 -20 replies
25/29iommu/tegra241-cmdqv: Use request_threaded_irqdrivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c+3 -20 replies
26/29iommu/tegra241-cmdqv: Simplify deinit flow in tegra241_cmdqv_remove_vintf()drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c+3 -100 replies
27/29iommu/tegra241-cmdqv: Do not statically map LVCMDQsdrivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c+33 -40 replies
28/29iommu/tegra241-cmdqv: Add user-space use supportdrivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c +1 more+465 -60 replies
29/29iommu/tegra241-cmdqv: Add IOMMU_VEVENTQ_TYPE_TEGRA241_CMDQV supportdrivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c include/uapi/linux/iommufd.h+37 -00 replies

Reviews trailers per patch

All 29 patches have a Reviewed-by or Acked-by.

PatchKevin TianJason GunthorpePranjal ShrivastavaVasant HegdeLu BaoluAlok Tiwari
1/29R-b R-b
2/29R-b
3/29R-b R-b R-b R-b
4/29R-b R-b R-b
5/29R-b R-b R-b R-b
6/29R-b R-b R-b R-b R-b
7/29R-b R-b A-b R-b R-b A-b
8/29R-b R-b
9/29R-b R-b
10/29R-b R-b
11/29R-b R-b R-b R-b
12/29R-b R-b R-b R-b R-b
13/29R-b R-b R-b R-b R-b
14/29R-b R-b R-b
15/29R-b R-b R-b
16/29R-b
17/29R-b R-b R-b R-b
18/29R-b
19/29R-b R-b R-b
20/29R-b R-b R-b R-b
21/29R-b R-b R-b R-b
22/29R-b
23/29R-b R-b R-b
24/29R-b R-b
25/29R-b R-b A-b
26/29R-b R-b A-b
27/29R-b A-b
28/29R-b R-b
29/29R-b R-b R-b

Trailers given in replies are filled; those carried in the patches from earlier versions are outlined with dashes. Scroll the table sideways for all 6 reviewers.

Read the v9 thread

Cover letter [PATCH v9 00/29] iommufd: Add vIOMMU infrastructure (Part-4 HW QUEUE)

The vIOMMU object is designed to represent a slice of an IOMMU HW for its virtualization features shared with or passed to user space (a VM mostly) in a way of HW acceleration. This extended the HWPT-based design for more advanced virtualization feature.

HW QUEUE introduced by this series as a part of the vIOMMU infrastructure represents a HW accelerated queue/buffer for VM to use exclusively, e.g.
- NVIDIA's Virtual Command Queue
- AMD vIOMMU's Command Buffer, Event Log Buffer, and PPR Log Buffer
each of which allows its IOMMU HW to directly access a queue memory owned by a guest VM and allows a guest OS to control the HW queue direclty, to avoid VM Exit overheads to improve the performance.

Introduce IOMMUFD_OBJ_HW_QUEUE and its pairing IOMMUFD_CMD_HW_QUEUE_ALLOC allowing VMM to forward the IOMMU-specific queue info, such as queue base address, size, and etc.

Meanwhile, a guest-owned queue needs the guest kernel to control the queue by reading/writing its consumer and producer indexes, via MMIO acceses to the hardware MMIO registers. Introduce an mmap infrastructure for iommufd to support passing through a piece of MMIO region from the host physical address space to the guest physical address space. The mmap info (offset/ length) used by an mmap syscall must be pre-allocated and returned to the user space via an output driver-data during an IOMMUFD_CMD_HW_QUEUE_ALLOC call. Thus, it requires a driver-specific user data support in the vIOMMU allocation flow.

As a real-world use case, this series implements a HW QUEUE support in the tegra241-cmdqv driver for VCMDQs on NVIDIA Grace CPU. In another word, it is also the Tegra CMDQV series Part-2 (user-space support), reworked from Previous RFCv1:

    https://lore.kernel.org/all/[email protected]/

This enables the HW accelerated feature for NVIDIA Grace CPU. Compared to the standard SMMUv3 operating in the nested translation mode trapping CMDQ for TLBI and ATC_INV commands, this gives a huge performance improvement:
70% to 90% reductions of invalidation time were measured by various DMA unmap tests running in a guest OS.

// Unmap latencies from "dma_map_benchmark -g @granule -t @threads", // by toggling "/sys/kernel/debug/iommu/tegra241_cmdqv/bypass_vcmdq" @granule | @threads | bypass_vcmdq=1 | bypass_vcmdq=0

    4KB        1          35.7 us          5.3 us
   16KB        1          41.8 us          6.8 us
   64KB        1          68.9 us          9.9 us
  128KB        1         109.0 us         12.6 us
  256KB        1         187.1 us         18.0 us
    4KB        2          96.9 us          6.8 us
   16KB        2          97.8 us          7.5 us
   64KB        2         151.5 us         10.7 us
  128KB        2         257.8 us         12.7 us
  256KB        2         443.0 us         17.9 us

This is on Github:
https://github.com/nicolinc/iommufd/commits/iommufd_hw_queue-v9

Paring QEMU branch for testing (reusing v8):
https://github.com/nicolinc/qemu/commits/wip/for_iommufd_hw_queue-v8

Changelog
v9 (attached git-diff v8..v9 at the end of this letter)
* Add Reviewed-by from Vasant and Jason
* [iommufd] Fix offset calculation
* [iommufd] Add unaligned iova/length selftest coverage for hw_queue
* [iommufd] Pass in aligned iova/length to iommufd_access_pin_pages()
* [smmu] Change "u32 *type" at arm_smmu_hw_info() in the header
v8
https://lore.kernel.org/all/[email protected]/
* Add Reviewed-by from Pranj, Kevin and Jason
* Improve kdoc and comments
* [iommufd] Skip selftest for no_viommu variants
* [iommufd] Add unmap coverage for non internal area
* [iommufd] Skip the first page when mtree_alloc_range()
* [iommufd] Correct the passed in index to mtree_erase()
* [iommufd] Correct variable types in iommufd_hw_queue_alloc_phys()
* [iommufd] Reject iopt_unmap_iova_range() if area->num_locks is set
* [tegra] Rename "SID replacement" with "SID mapping"
* [tegra] Unwrap useless _tegra241_vcmdq_hw_init helper
v7
https://lore.kernel.org/all/[email protected]/
* Rebased on Jason's for-next tree (iommufd_hw_queue-prep series)
* Add Reviewed-by from Baolu, Jason, Pranjal
* Update kdocs and notes
* [iommu] Replace "u32" with "enum iommu_hw_info_type"
* [iommufd] Rename vdev->id to vdev->virt_id
* [iommufd] Replace macros with inline helpers
* [iommufd] Report unmapped_bytes in error path
* [iommufd] Add iommufd_access_is_internal helper
* [iommufd] Do not drop ops->unmap check for mdevs
* [iommufd] Store physical addresses in immap structure
* [iommufd] Reorder access and hw_queue object allocations
* [iommufd] Scan for an internal access before any unmap call
* [iommufd] Drop unused ictx pointer in struct iommufd_hw_queue
* [iommufd] Use kcalloc to avoid failure due to memory fragmentation
* [tegra] Use "else"
* [tegra] Lock destroy() using lvcmdq_mutex
v6
https://lore.kernel.org/all/[email protected]/
* Rebase on iommufd_hw_queue-prep-v2
* Add Reviewed-by from Kevin and Jason
* [iommufd] Update kdocs and notes
* [iommufd] Drop redundant pages[i] check
* [iommufd] Allow nesting_parent_iova to be 0
* [iommufd] Add iommufd_hw_queue_alloc_phys()
* [iommufd] Revise iommufd_viommu_alloc/destroy_mmap APIs
* [iommufd] Move destroy ops to vdevice/hw_queue structures
* [iommufd] Add union in hw_info struct to share out_data_type field
* [iommufd] Replace iopt_pin/unpin_pages() with internal access APIs
* [iommufd] Replace vdevice_alloc with vdevice_size and vdevice_init
* [iommufd] Replace hw_queue_alloc with get_hw_queue_size/hw_queue_init
* [iommufd] Replace IOMMUFD_VIOMMU_FLAG_HW_QUEUE_READS_PA with init_phys
* [smmu] Drop arm_smmu_domain_ipa_to_pa
* [smmu] Update arm_smmu_impl_ops changes for vsmmu_init
* [tegra] Add a vdev_to_vsid macro
* [tegra] Add lvcmdq_mutex to protect multi queues
* [tegra] Drop duplicated kcalloc for vintf->lvcmdqs (memory leak)
v5
https://lore.kernel.org/all/[email protected]/
* Rebase on v6.15-rc6
* Add Reviewed-by from Jason and Kevin
* Correct typos in kdoc and update commit logs
* [iommufd] Add a cosmetic fix
* [iommufd] Drop unused num_pfns
* [iommufd] Drop unnecessary check
* [iommufd] Reorder patch sequence
* [iommufd] Use io_remap_pfn_range()
* [iommufd] Use success oriented flow
* [iommufd] Fix max_npages calculation
* [iommufd] Add more selftest coverage
* [iommufd] Drop redundant static_assert
* [iommufd] Fix mmap pfn range validation
* [iommufd] Reject unmap on pinned iovas
* [iommufd] Drop redundant vm_flags_set()
* [iommufd] Drop iommufd_struct_destroy()
* [iommufd] Drop redundant queue iova test
* [iommufd] Use "mmio_addr" and "mmio_pfn"
* [iommufd] Rename to "nesting_parent_iova"
* [iommufd] Make iopt_pin_pages call option
* [iommufd] Add ictx comparison in depend()
* [iommufd] Add iommufd_object_alloc_ucmd()
* [iommufd] Move kcalloc() after validations
* [iommufd] Replace ictx setting with WARN_ON
* [iommufd] Make hw_info's type bidirectional
* [smmu] Add supported_vsmmu_type in impl_ops
* [smmu] Drop impl report in smmu vendor struct
* [tegra] Add IOMMU_HW_INFO_TYPE_TEGRA241_CMDQV
* [tegra] Replace "number of VINTFs" with a note
* [tegra] Drop the redundant lvcmdq pointer setting
* [tegra] Flag IOMMUFD_VIOMMU_FLAG_HW_QUEUE_READS_PA
* [tegra] Use "vintf_alloc_vsid" for vdevice_alloc op
v4
https://lore.kernel.org/all/[email protected]/
* Rebase on v6.15-rc5
* Add Reviewed-by from Vasant
* Rename "vQUEUE" to "HW QUEUE"
* Use "offset" and "length" for all mmap-related variables
* [iommufd] Use u64 for guest PA
* [iommufd] Fix typo in uAPI doc
* [iommufd] Rename immap_id to offset
* [iommufd] Drop the partial-size mmap support
* [iommufd] Do not replace WARN_ON with WARN_ON_ONCE
* [iommufd] Use "u64 base_addr" for queue base address
* [iommufd] Use u64 base_pfn/num_pfns for immap structure
* [iommufd] Correct the size passed in to mtree_alloc_range()
* [iommufd] Add IOMMUFD_VIOMMU_FLAG_HW_QUEUE_READS_PA to viommu_ops
v3
https://lore.kernel.org/all/[email protected]/
* Add Reviewed-by from Baolu, Pranjal, and Alok
* Revise kdocs, uAPI docs, and commit logs
* Rename "vCMDQ" back to "vQUEUE" for AMD cases
* [tegra] Add tegra241_vcmdq_hw_flush_timeout()
* [tegra] Rename vsmmu_alloc to alloc_vintf_user
* [tegra] Use writel for SID replacement registers
* [tegra] Move mmap removal call to vsmmu_destroy op
* [tegra] Fix revert in tegra241_vintf_alloc_lvcmdq_user()
* [iommufd] Replace "& ~PAGE_MASK" with PAGE_ALIGNED()
* [iommufd] Add an object-type "owner" to immap structure
* [iommufd] Drop the ictx input in the new for-driver APIs
* [iommufd] Add iommufd_vma_ops to keep track of mmap lifecycle
* [iommufd] Add viommu-based iommufd_viommu_alloc/destroy_mmap helpers
* [iommufd] Rename iommufd_ctx_alloc/free_mmap to _iommufd_alloc/destroy_mmap
v2
https://lore.kernel.org/all/[email protected]/
* Add Reviewed-by from Jason
* [smmu] Fix vsmmu initial value
* [smmu] Support impl for hw_info
* [tegra] Rename "slot" to "vsid"
* [tegra] Update kdocs and commit logs
* [tegra] Map/unmap LVCMDQ dynamically
* [tegra] Refcount the previous LVCMDQ
* [tegra] Return -EEXIST if LVCMDQ exists
* [tegra] Simplify VINTF cleanup routine
* [tegra] Use vmid and s2_domain in vsmmu
* [tegra] Rename "mmap_pgoff" to "immap_id"
* [tegra] Add more addr and length validation
* [iommufd] Add more narrative to mmap's kdoc
* [iommufd] Add iommufd_struct_depend/undepend()
* [iommufd] Rename vcmdq_free op to vcmdq_destroy
* [iommufd] Fix bug in iommu_copy_struct_to_user()
* [iommufd] Drop is_io from iommufd_ctx_alloc_mmap()
* [iommufd] Test the queue memory for its contiguity
* [iommufd] Return -ENXIO if address or length fails
* [iommufd] Do not change @min_last in mock_viommu_alloc()
* [iommufd] Generalize TEGRA241_VCMDQ data in core structure
* [iommufd] Add selftest coverage for IOMMUFD_CMD_VCMDQ_ALLOC
* [iommufd] Add iopt_pin_pages() to prevent queue memory from unmapping
v1
https://lore.kernel.org/all/[email protected]/

Thanks
Nicolin

Nicolin Chen (29):
iommufd: Report unmapped bytes in the error path of
iopt_unmap_iova_range
iommufd: Correct virt_id kdoc at struct iommu_vdevice_alloc iommufd/viommu: Explicitly define vdev->virt_id
iommu: Use enum iommu_hw_info_type for type in hw_info op
iommu: Add iommu_copy_struct_to_user helper
iommu: Pass in a driver-level user data structure to viommu_init op iommufd/viommu: Allow driver-specific user data for a vIOMMU object iommufd/selftest: Support user_data in mock_viommu_alloc iommufd/selftest: Add coverage for viommu data iommufd/access: Add internal APIs for HW queue to use iommufd/access: Bypass access->ops->unmap for internal use iommufd/viommu: Add driver-defined vDEVICE support iommufd/viommu: Introduce IOMMUFD_OBJ_HW_QUEUE and its related struct iommufd/viommu: Add IOMMUFD_CMD_HW_QUEUE_ALLOC ioctl iommufd/driver: Add iommufd_hw_queue_depend/undepend() helpers iommufd/selftest: Add coverage for IOMMUFD_CMD_HW_QUEUE_ALLOC
iommufd: Add mmap interface
iommufd/selftest: Add coverage for the new mmap interface
Documentation: userspace-api: iommufd: Update HW QUEUE
iommu: Allow an input type in hw_info op
iommufd: Allow an input data_type via iommu_hw_info iommufd/selftest: Update hw_info coverage for an input data_type iommu/arm-smmu-v3-iommufd: Add vsmmu_size/type and vsmmu_init impl ops iommu/arm-smmu-v3-iommufd: Add hw_info to impl_ops iommu/tegra241-cmdqv: Use request_threaded_irq iommu/tegra241-cmdqv: Simplify deinit flow in

    tegra241_cmdqv_remove_vintf()

iommu/tegra241-cmdqv: Do not statically map LVCMDQs iommu/tegra241-cmdqv: Add user-space use support iommu/tegra241-cmdqv: Add IOMMU_VEVENTQ_TYPE_TEGRA241_CMDQV support

 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h   |  25 +-
 drivers/iommu/iommufd/io_pagetable.h          |   5 +-
 drivers/iommu/iommufd/iommufd_private.h       |  46 +-
 drivers/iommu/iommufd/iommufd_test.h          |  20 +
 include/linux/iommu.h                         |  50 +-
 include/linux/iommufd.h                       | 160 ++++++
 include/uapi/linux/iommufd.h                  | 147 +++++-
 tools/testing/selftests/iommu/iommufd_utils.h |  89 +++-
 .../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c     |  28 +-
 .../iommu/arm/arm-smmu-v3/tegra241-cmdqv.c    | 477 +++++++++++++++++-
 drivers/iommu/intel/iommu.c                   |   7 +-
 drivers/iommu/iommufd/device.c                |  87 +++-
 drivers/iommu/iommufd/driver.c                |  82 ++-
 drivers/iommu/iommufd/io_pagetable.c          |  13 +-
 drivers/iommu/iommufd/main.c                  |  69 +++
 drivers/iommu/iommufd/pages.c                 |  12 +-
 drivers/iommu/iommufd/selftest.c              | 153 +++++-
 drivers/iommu/iommufd/viommu.c                | 218 +++++++-
 tools/testing/selftests/iommu/iommufd.c       | 141 +++++-
 .../selftests/iommu/iommufd_fail_nth.c        |  15 +-
 Documentation/userspace-api/iommufd.rst       |  12 +
 21 files changed, 1745 insertions(+), 111 deletions(-)
Signature · 1 line
2.43.0
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +2 -1
index aa25156e04a3..3fa02c51df9f 100644
@@ -1045,7 +1045,8 @@ struct arm_vsmmu {
 };
 
 #if IS_ENABLED(CONFIG_ARM_SMMU_V3_IOMMUFD)
-void *arm_smmu_hw_info(struct device *dev, u32 *length, u32 *type);
+void *arm_smmu_hw_info(struct device *dev, u32 *length,
+		       enum iommu_hw_info_type *type);
 size_t arm_smmu_get_viommu_size(struct device *dev,
 				enum iommu_viommu_type viommu_type);
 int arm_vsmmu_init(struct iommufd_viommu *viommu,
drivers/iommu/iommufd/viommu.c +13 -10
index 00641204efb2..91339f799916 100644
@@ -206,7 +206,11 @@ static void iommufd_hw_queue_destroy_access(struct iommufd_ctx *ictx,
 					    struct iommufd_access *access,
 					    u64 base_iova, size_t length)
 {
-	iommufd_access_unpin_pages(access, base_iova, length);
+	u64 aligned_iova = PAGE_ALIGN_DOWN(base_iova);
+	u64 offset = base_iova - aligned_iova;
+
+	iommufd_access_unpin_pages(access, aligned_iova,
+				   PAGE_ALIGN(length + offset));
 	iommufd_access_detach_internal(access);
 	iommufd_access_destroy_internal(ictx, access);
 }
@@ -239,22 +243,23 @@ static struct iommufd_access *
 iommufd_hw_queue_alloc_phys(struct iommu_hw_queue_alloc *cmd,
 			    struct iommufd_viommu *viommu, phys_addr_t *base_pa)
 {
+	u64 aligned_iova = PAGE_ALIGN_DOWN(cmd->nesting_parent_iova);
+	u64 offset = cmd->nesting_parent_iova - aligned_iova;
 	struct iommufd_access *access;
 	struct page **pages;
 	size_t max_npages;
 	size_t length;
-	u64 offset;
 	size_t i;
 	int rc;
 
-	offset =
-		cmd->nesting_parent_iova - PAGE_ALIGN(cmd->nesting_parent_iova);
-	/* DIV_ROUND_UP(offset + cmd->length, PAGE_SIZE) */
+	/* max_npages = DIV_ROUND_UP(offset + cmd->length, PAGE_SIZE) */
 	if (check_add_overflow(offset, cmd->length, &length))
 		return ERR_PTR(-ERANGE);
 	if (check_add_overflow(length, PAGE_SIZE - 1, &length))
 		return ERR_PTR(-ERANGE);
 	max_npages = length / PAGE_SIZE;
+	/* length needs to be page aligned too */
+	length = max_npages * PAGE_SIZE;
 
 	/*
 	 * Use kvcalloc() to avoid memory fragmentation for a large page array.
@@ -274,8 +279,7 @@ iommufd_hw_queue_alloc_phys(struct iommu_hw_queue_alloc *cmd,
 	if (rc)
 		goto out_destroy;
 
-	rc = iommufd_access_pin_pages(access, cmd->nesting_parent_iova,
-				      cmd->length, pages, 0);
+	rc = iommufd_access_pin_pages(access, aligned_iova, length, pages, 0);
 	if (rc)
 		goto out_detach;
 
@@ -287,13 +291,12 @@ iommufd_hw_queue_alloc_phys(struct iommu_hw_queue_alloc *cmd,
 		goto out_unpin;
 	}
 
-	*base_pa = page_to_pfn(pages[0]) << PAGE_SHIFT;
+	*base_pa = (page_to_pfn(pages[0]) << PAGE_SHIFT) + offset;
 	kfree(pages);
 	return access;
 
 out_unpin:
-	iommufd_access_unpin_pages(access, cmd->nesting_parent_iova,
-				   cmd->length);
+	iommufd_access_unpin_pages(access, aligned_iova, length);
 out_detach:
 	iommufd_access_detach_internal(access);
 out_destroy:
tools/testing/selftests/iommu/iommufd.c +5 -4
index 9d5b852d5e19..d59d48022a24 100644
@@ -3104,17 +3104,18 @@ TEST_F(iommufd_viommu, hw_queue)
 	/* Allocate index=0, declare ownership of the iova */
 	test_cmd_hw_queue_alloc(viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST, 0,
 				iova, PAGE_SIZE, &hw_queue_id[0]);
-	/* Fail duplicate */
+	/* Fail duplicated index */
 	test_err_hw_queue_alloc(EEXIST, viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST,
 				0, iova, PAGE_SIZE, &hw_queue_id[0]);
 	/* Fail unmap, due to iova ownership */
 	test_err_ioctl_ioas_unmap(EBUSY, iova, PAGE_SIZE);
 	/* The 2nd page is not pinned, so it can be unmmap */
-	test_ioctl_ioas_unmap(iova + PAGE_SIZE, PAGE_SIZE);
+	test_ioctl_ioas_unmap(iova2, PAGE_SIZE);
 
-	/* Allocate index=1 */
+	/* Allocate index=1, with an unaligned case */
 	test_cmd_hw_queue_alloc(viommu_id, IOMMU_HW_QUEUE_TYPE_SELFTEST, 1,
-				iova, PAGE_SIZE, &hw_queue_id[1]);
+				iova + PAGE_SIZE / 2, PAGE_SIZE / 2,
+				&hw_queue_id[1]);
 	/* Fail to destroy, due to dependency */
 	EXPECT_ERRNO(EBUSY, _test_ioctl_destroy(self->fd, hw_queue_id[0]));