iommu/amd: Miscellaneous clean up when free domain
authorSuravee Suthikulpanit <suravee.suthikulpanit@amd.com>
Thu, 21 Sep 2023 09:21:40 +0000 (09:21 +0000)
committerJoerg Roedel <jroedel@suse.de>
Mon, 25 Sep 2023 10:39:02 +0000 (12:39 +0200)
* Use the protection_domain_free() helper function to free domain.
  The function has been modified to also free memory used for the v1 and v2
  page tables. Also clear gcr3 table in v2 page table free path.

* Refactor code into cleanup_domain() for reusability. Change BUG_ON to
  WARN_ON in cleanup path.

* Protection domain dev_cnt should be read when the domain is locked.

Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
Co-developed-by: Vasant Hegde <vasant.hegde@amd.com>
Signed-off-by: Vasant Hegde <vasant.hegde@amd.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Jerry Snitselaar <jsnitsel@redhat.com>
Link: https://lore.kernel.org/r/20230921092147.5930-8-vasant.hegde@amd.com
Signed-off-by: Joerg Roedel <jroedel@suse.de>
drivers/iommu/amd/io_pgtable_v2.c
drivers/iommu/amd/iommu.c

index e9ef2e0a62f670096cc317876180f1d888fd6e07..f818a7e254d42627ebbd2d3154e290263af37421 100644 (file)
@@ -363,10 +363,10 @@ static void v2_free_pgtable(struct io_pgtable *iop)
        if (!(pdom->flags & PD_IOMMUV2_MASK))
                return;
 
-       /*
-        * Make changes visible to IOMMUs. No need to clear gcr3 entry
-        * as gcr3 table is already freed.
-        */
+       /* Clear gcr3 entry */
+       amd_iommu_domain_clear_gcr3(&pdom->domain, 0);
+
+       /* Make changes visible to IOMMUs */
        amd_iommu_domain_update(pdom);
 
        /* Free page table */
index 46a10b489cf59e19acf5fd9562f8543ceaba2b96..0dd8390113afaeea464fe92a8049bc302a40ef91 100644 (file)
@@ -2047,9 +2047,11 @@ void amd_iommu_domain_update(struct protection_domain *domain)
 static void cleanup_domain(struct protection_domain *domain)
 {
        struct iommu_dev_data *entry;
-       unsigned long flags;
 
-       spin_lock_irqsave(&domain->lock, flags);
+       lockdep_assert_held(&domain->lock);
+
+       if (!domain->dev_cnt)
+               return;
 
        while (!list_empty(&domain->dev_list)) {
                entry = list_first_entry(&domain->dev_list,
@@ -2057,8 +2059,7 @@ static void cleanup_domain(struct protection_domain *domain)
                BUG_ON(!entry->domain);
                do_detach(entry);
        }
-
-       spin_unlock_irqrestore(&domain->lock, flags);
+       WARN_ON(domain->dev_cnt != 0);
 }
 
 static void protection_domain_free(struct protection_domain *domain)
@@ -2069,6 +2070,12 @@ static void protection_domain_free(struct protection_domain *domain)
        if (domain->iop.pgtbl_cfg.tlb)
                free_io_pgtable_ops(&domain->iop.iop.ops);
 
+       if (domain->flags & PD_IOMMUV2_MASK)
+               free_gcr3_table(domain);
+
+       if (domain->iop.root)
+               free_page((unsigned long)domain->iop.root);
+
        if (domain->id)
                domain_id_free(domain->id);
 
@@ -2083,10 +2090,8 @@ static int protection_domain_init_v1(struct protection_domain *domain, int mode)
 
        if (mode != PAGE_MODE_NONE) {
                pt_root = (void *)get_zeroed_page(GFP_KERNEL);
-               if (!pt_root) {
-                       domain_id_free(domain->id);
+               if (!pt_root)
                        return -ENOMEM;
-               }
        }
 
        amd_iommu_domain_set_pgtable(domain, pt_root, mode);
@@ -2100,10 +2105,8 @@ static int protection_domain_init_v2(struct protection_domain *domain)
 
        domain->domain.pgsize_bitmap = AMD_IOMMU_PGSIZES_V2;
 
-       if (setup_gcr3_table(domain, 1)) {
-               domain_id_free(domain->id);
+       if (setup_gcr3_table(domain, 1))
                return -ENOMEM;
-       }
 
        return 0;
 }
@@ -2162,14 +2165,12 @@ static struct protection_domain *protection_domain_alloc(unsigned int type)
                goto out_err;
 
        pgtbl_ops = alloc_io_pgtable_ops(pgtable, &domain->iop.pgtbl_cfg, domain);
-       if (!pgtbl_ops) {
-               domain_id_free(domain->id);
+       if (!pgtbl_ops)
                goto out_err;
-       }
 
        return domain;
 out_err:
-       kfree(domain);
+       protection_domain_free(domain);
        return NULL;
 }
 
@@ -2207,19 +2208,18 @@ static struct iommu_domain *amd_iommu_domain_alloc(unsigned type)
 static void amd_iommu_domain_free(struct iommu_domain *dom)
 {
        struct protection_domain *domain;
+       unsigned long flags;
 
-       domain = to_pdomain(dom);
+       if (!dom)
+               return;
 
-       if (domain->dev_cnt > 0)
-               cleanup_domain(domain);
+       domain = to_pdomain(dom);
 
-       BUG_ON(domain->dev_cnt != 0);
+       spin_lock_irqsave(&domain->lock, flags);
 
-       if (!dom)
-               return;
+       cleanup_domain(domain);
 
-       if (domain->flags & PD_IOMMUV2_MASK)
-               free_gcr3_table(domain);
+       spin_unlock_irqrestore(&domain->lock, flags);
 
        protection_domain_free(domain);
 }