| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES |
| */ |
| #include <linux/iommu.h> |
| |
| #include "iommufd_private.h" |
| |
| void iommufd_hw_pagetable_destroy(struct iommufd_object *obj) |
| { |
| struct iommufd_hw_pagetable *hwpt = |
| container_of(obj, struct iommufd_hw_pagetable, obj); |
| |
| WARN_ON(!list_empty(&hwpt->devices)); |
| |
| if (!list_empty(&hwpt->hwpt_item)) { |
| mutex_lock(&hwpt->ioas->mutex); |
| list_del(&hwpt->hwpt_item); |
| mutex_unlock(&hwpt->ioas->mutex); |
| |
| iopt_table_remove_domain(&hwpt->ioas->iopt, hwpt->domain); |
| } |
| |
| if (hwpt->domain) |
| iommu_domain_free(hwpt->domain); |
| |
| refcount_dec(&hwpt->ioas->obj.users); |
| mutex_destroy(&hwpt->devices_lock); |
| } |
| |
| /** |
| * iommufd_hw_pagetable_alloc() - Get an iommu_domain for a device |
| * @ictx: iommufd context |
| * @ioas: IOAS to associate the domain with |
| * @idev: Device to get an iommu_domain for |
| * @immediate_attach: True if idev should be attached to the hwpt |
| * |
| * Allocate a new iommu_domain and return it as a hw_pagetable. The HWPT |
| * will be linked to the given ioas and upon return the underlying iommu_domain |
| * is fully popoulated. |
| */ |
| struct iommufd_hw_pagetable * |
| iommufd_hw_pagetable_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas, |
| struct iommufd_device *idev, bool immediate_attach) |
| { |
| struct iommufd_hw_pagetable *hwpt; |
| int rc; |
| |
| lockdep_assert_held(&ioas->mutex); |
| |
| hwpt = iommufd_object_alloc(ictx, hwpt, IOMMUFD_OBJ_HW_PAGETABLE); |
| if (IS_ERR(hwpt)) |
| return hwpt; |
| |
| INIT_LIST_HEAD(&hwpt->devices); |
| INIT_LIST_HEAD(&hwpt->hwpt_item); |
| mutex_init(&hwpt->devices_lock); |
| /* Pairs with iommufd_hw_pagetable_destroy() */ |
| refcount_inc(&ioas->obj.users); |
| hwpt->ioas = ioas; |
| |
| hwpt->domain = iommu_domain_alloc(idev->dev->bus); |
| if (!hwpt->domain) { |
| rc = -ENOMEM; |
| goto out_abort; |
| } |
| |
| mutex_lock(&hwpt->devices_lock); |
| |
| /* |
| * immediate_attach exists only to accommodate iommu drivers that cannot |
| * directly allocate a domain. These drivers do not finish creating the |
| * domain until attach is completed. Thus we must have this call |
| * sequence. Once those drivers are fixed this should be removed. |
| */ |
| if (immediate_attach) { |
| rc = iommufd_hw_pagetable_attach(hwpt, idev); |
| if (rc) |
| goto out_unlock; |
| } |
| |
| rc = iopt_table_add_domain(&hwpt->ioas->iopt, hwpt->domain); |
| if (rc) |
| goto out_detach; |
| list_add_tail(&hwpt->hwpt_item, &hwpt->ioas->hwpt_list); |
| |
| if (immediate_attach) { |
| /* See iommufd_device_do_attach() */ |
| refcount_inc(&hwpt->obj.users); |
| idev->hwpt = hwpt; |
| list_add(&idev->devices_item, &hwpt->devices); |
| } |
| |
| mutex_unlock(&hwpt->devices_lock); |
| return hwpt; |
| |
| out_detach: |
| if (immediate_attach) |
| iommufd_hw_pagetable_detach(hwpt, idev); |
| out_unlock: |
| mutex_unlock(&hwpt->devices_lock); |
| out_abort: |
| iommufd_object_abort_and_destroy(ictx, &hwpt->obj); |
| return ERR_PTR(rc); |
| } |