]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
iommufd/selftest: Hugepage mock domain support
authorJoao Martins <joao.m.martins@oracle.com>
Fri, 2 Feb 2024 13:34:13 +0000 (13:34 +0000)
committerJason Gunthorpe <jgg@nvidia.com>
Tue, 6 Feb 2024 15:31:45 +0000 (11:31 -0400)
Add support to mock iommu hugepages of 1M (for a 2K mock io page size). To
avoid breaking test suite defaults, the way this is done is by explicitly
creating a iommu mock device which has hugepage support (i.e. through
MOCK_FLAGS_DEVICE_HUGE_IOVA).

The same scheme is maintained of mock base page index tracking in the
XArray, except that an extra bit is added to mark it as a hugepage. One
subpage containing the dirty bit, means that the whole hugepage is dirty
(similar to AMD IOMMU non-standard page sizes). For clearing, same thing
applies, and it must clear all dirty subpages.

This is in preparation for dirty tracking to mark mock hugepages as
dirty to exercise all the iova-bitmap fixes.

Link: https://lore.kernel.org/r/20240202133415.23819-8-joao.m.martins@oracle.com
Signed-off-by: Joao Martins <joao.m.martins@oracle.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
drivers/iommu/iommufd/iommufd_test.h
drivers/iommu/iommufd/selftest.c

index 482d4059f5db6aed38ee8aa60f25b791f1e7556d..e854d3f672051b5223e0fec8af741abf03bbffbd 100644 (file)
@@ -45,6 +45,7 @@ enum {
 
 enum {
        MOCK_FLAGS_DEVICE_NO_DIRTY = 1 << 0,
+       MOCK_FLAGS_DEVICE_HUGE_IOVA = 1 << 1,
 };
 
 enum {
index 796e7e3ec0cfd867ef0f2a771c22f78bdab5ed40..8abf9747773eb845fc7d4a96c7c0ebfaaa9a4ef9 100644 (file)
@@ -41,6 +41,7 @@ static atomic_t mock_dev_num;
 enum {
        MOCK_DIRTY_TRACK = 1,
        MOCK_IO_PAGE_SIZE = PAGE_SIZE / 2,
+       MOCK_HUGE_PAGE_SIZE = 512 * MOCK_IO_PAGE_SIZE,
 
        /*
         * Like a real page table alignment requires the low bits of the address
@@ -53,6 +54,7 @@ enum {
        MOCK_PFN_START_IOVA = _MOCK_PFN_START,
        MOCK_PFN_LAST_IOVA = _MOCK_PFN_START,
        MOCK_PFN_DIRTY_IOVA = _MOCK_PFN_START << 1,
+       MOCK_PFN_HUGE_IOVA = _MOCK_PFN_START << 2,
 };
 
 /*
@@ -242,6 +244,8 @@ static int mock_domain_read_and_clear_dirty(struct iommu_domain *domain,
                        continue;
                }
 
+               if (xa_to_value(ent) & MOCK_PFN_HUGE_IOVA)
+                       pgsize = MOCK_HUGE_PAGE_SIZE;
                head = iova & ~(pgsize - 1);
 
                /* Clear dirty */
@@ -260,6 +264,7 @@ const struct iommu_dirty_ops dirty_ops = {
 
 static struct iommu_domain *mock_domain_alloc_paging(struct device *dev)
 {
+       struct mock_dev *mdev = container_of(dev, struct mock_dev, dev);
        struct mock_iommu_domain *mock;
 
        mock = kzalloc(sizeof(*mock), GFP_KERNEL);
@@ -268,6 +273,8 @@ static struct iommu_domain *mock_domain_alloc_paging(struct device *dev)
        mock->domain.geometry.aperture_start = MOCK_APERTURE_START;
        mock->domain.geometry.aperture_end = MOCK_APERTURE_LAST;
        mock->domain.pgsize_bitmap = MOCK_IO_PAGE_SIZE;
+       if (dev && mdev->flags & MOCK_FLAGS_DEVICE_HUGE_IOVA)
+               mock->domain.pgsize_bitmap |= MOCK_HUGE_PAGE_SIZE;
        mock->domain.ops = mock_ops.default_domain_ops;
        mock->domain.type = IOMMU_DOMAIN_UNMANAGED;
        xa_init(&mock->pfns);
@@ -313,7 +320,7 @@ mock_domain_alloc_user(struct device *dev, u32 flags,
                        return ERR_PTR(-EOPNOTSUPP);
                if (user_data || (has_dirty_flag && no_dirty_ops))
                        return ERR_PTR(-EOPNOTSUPP);
-               domain = mock_domain_alloc_paging(NULL);
+               domain = mock_domain_alloc_paging(dev);
                if (!domain)
                        return ERR_PTR(-ENOMEM);
                if (has_dirty_flag)
@@ -376,6 +383,9 @@ static int mock_domain_map_pages(struct iommu_domain *domain,
 
                        if (pgcount == 1 && cur + MOCK_IO_PAGE_SIZE == pgsize)
                                flags = MOCK_PFN_LAST_IOVA;
+                       if (pgsize != MOCK_IO_PAGE_SIZE) {
+                               flags |= MOCK_PFN_HUGE_IOVA;
+                       }
                        old = xa_store(&mock->pfns, iova / MOCK_IO_PAGE_SIZE,
                                       xa_mk_value((paddr / MOCK_IO_PAGE_SIZE) |
                                                   flags),
@@ -630,7 +640,8 @@ static struct mock_dev *mock_dev_create(unsigned long dev_flags)
        struct mock_dev *mdev;
        int rc;
 
-       if (dev_flags & ~(MOCK_FLAGS_DEVICE_NO_DIRTY))
+       if (dev_flags &
+           ~(MOCK_FLAGS_DEVICE_NO_DIRTY | MOCK_FLAGS_DEVICE_HUGE_IOVA))
                return ERR_PTR(-EINVAL);
 
        mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);