Commit 91a2e17e authored by Jason Gunthorpe's avatar Jason Gunthorpe

iommufd: Replace the hwpt->devices list with iommufd_group

The devices list was used as a simple way to avoid having per-group
information. Now that this seems to be unavoidable, just commit to
per-group information fully and remove the devices list from the HWPT.

The iommufd_group stores the currently assigned HWPT for the entire group
and we can manage the per-device attach/detach with a list in the
iommufd_group.

For destruction the flow is organized to make the following patches
easier, the actual call to iommufd_object_destroy_user() is done at the
top of the call chain without holding any locks. The HWPT to be destroyed
is returned out from the locked region to make this possible. Later
patches create locking that requires this.

Link: https://lore.kernel.org/r/3-v8-6659224517ea+532-iommufd_alloc_jgg@nvidia.comReviewed-by: default avatarLu Baolu <baolu.lu@linux.intel.com>
Reviewed-by: default avatarKevin Tian <kevin.tian@intel.com>
Tested-by: default avatarNicolin Chen <nicolinc@nvidia.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
parent 3a3329a7
...@@ -20,9 +20,12 @@ static void iommufd_group_release(struct kref *kref) ...@@ -20,9 +20,12 @@ static void iommufd_group_release(struct kref *kref)
struct iommufd_group *igroup = struct iommufd_group *igroup =
container_of(kref, struct iommufd_group, ref); container_of(kref, struct iommufd_group, ref);
WARN_ON(igroup->hwpt || !list_empty(&igroup->device_list));
xa_cmpxchg(&igroup->ictx->groups, iommu_group_id(igroup->group), igroup, xa_cmpxchg(&igroup->ictx->groups, iommu_group_id(igroup->group), igroup,
NULL, GFP_KERNEL); NULL, GFP_KERNEL);
iommu_group_put(igroup->group); iommu_group_put(igroup->group);
mutex_destroy(&igroup->lock);
kfree(igroup); kfree(igroup);
} }
...@@ -83,6 +86,8 @@ static struct iommufd_group *iommufd_get_group(struct iommufd_ctx *ictx, ...@@ -83,6 +86,8 @@ static struct iommufd_group *iommufd_get_group(struct iommufd_ctx *ictx,
} }
kref_init(&new_igroup->ref); kref_init(&new_igroup->ref);
mutex_init(&new_igroup->lock);
INIT_LIST_HEAD(&new_igroup->device_list);
/* group reference moves into new_igroup */ /* group reference moves into new_igroup */
new_igroup->group = group; new_igroup->group = group;
...@@ -320,29 +325,18 @@ static int iommufd_device_setup_msi(struct iommufd_device *idev, ...@@ -320,29 +325,18 @@ static int iommufd_device_setup_msi(struct iommufd_device *idev,
return 0; return 0;
} }
static bool iommufd_hw_pagetable_has_group(struct iommufd_hw_pagetable *hwpt,
struct iommufd_group *igroup)
{
struct iommufd_device *cur_dev;
lockdep_assert_held(&hwpt->devices_lock);
list_for_each_entry(cur_dev, &hwpt->devices, devices_item)
if (cur_dev->igroup->group == igroup->group)
return true;
return false;
}
int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt, int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
struct iommufd_device *idev) struct iommufd_device *idev)
{ {
phys_addr_t sw_msi_start = PHYS_ADDR_MAX; phys_addr_t sw_msi_start = PHYS_ADDR_MAX;
int rc; int rc;
lockdep_assert_held(&hwpt->devices_lock); mutex_lock(&idev->igroup->lock);
if (WARN_ON(idev->hwpt)) if (idev->igroup->hwpt != NULL && idev->igroup->hwpt != hwpt) {
return -EINVAL; rc = -EINVAL;
goto err_unlock;
}
/* /*
* Try to upgrade the domain we have, it is an iommu driver bug to * Try to upgrade the domain we have, it is an iommu driver bug to
...@@ -356,8 +350,9 @@ int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt, ...@@ -356,8 +350,9 @@ int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
hwpt->domain->ops->enforce_cache_coherency( hwpt->domain->ops->enforce_cache_coherency(
hwpt->domain); hwpt->domain);
if (!hwpt->enforce_cache_coherency) { if (!hwpt->enforce_cache_coherency) {
WARN_ON(list_empty(&hwpt->devices)); WARN_ON(list_empty(&idev->igroup->device_list));
return -EINVAL; rc = -EINVAL;
goto err_unlock;
} }
} }
...@@ -365,51 +360,52 @@ int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt, ...@@ -365,51 +360,52 @@ int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
idev->igroup->group, idev->igroup->group,
&sw_msi_start); &sw_msi_start);
if (rc) if (rc)
return rc; goto err_unlock;
rc = iommufd_device_setup_msi(idev, hwpt, sw_msi_start); rc = iommufd_device_setup_msi(idev, hwpt, sw_msi_start);
if (rc) if (rc)
goto err_unresv; goto err_unresv;
/* /*
* FIXME: Hack around missing a device-centric iommu api, only attach to * Only attach to the group once for the first device that is in the
* the group once for the first device that is in the group. * group. All the other devices will follow this attachment. The user
* should attach every device individually to the hwpt as the per-device
* reserved regions are only updated during individual device
* attachment.
*/ */
if (!iommufd_hw_pagetable_has_group(hwpt, idev->igroup)) { if (list_empty(&idev->igroup->device_list)) {
rc = iommu_attach_group(hwpt->domain, idev->igroup->group); rc = iommu_attach_group(hwpt->domain, idev->igroup->group);
if (rc) if (rc)
goto err_unresv; goto err_unresv;
idev->igroup->hwpt = hwpt;
} }
refcount_inc(&hwpt->obj.users);
list_add_tail(&idev->group_item, &idev->igroup->device_list);
mutex_unlock(&idev->igroup->lock);
return 0; return 0;
err_unresv: err_unresv:
iopt_remove_reserved_iova(&hwpt->ioas->iopt, idev->dev); iopt_remove_reserved_iova(&hwpt->ioas->iopt, idev->dev);
err_unlock:
mutex_unlock(&idev->igroup->lock);
return rc; return rc;
} }
void iommufd_hw_pagetable_detach(struct iommufd_hw_pagetable *hwpt, struct iommufd_hw_pagetable *
struct iommufd_device *idev) iommufd_hw_pagetable_detach(struct iommufd_device *idev)
{ {
if (!iommufd_hw_pagetable_has_group(hwpt, idev->igroup)) struct iommufd_hw_pagetable *hwpt = idev->igroup->hwpt;
mutex_lock(&idev->igroup->lock);
list_del(&idev->group_item);
if (list_empty(&idev->igroup->device_list)) {
iommu_detach_group(hwpt->domain, idev->igroup->group); iommu_detach_group(hwpt->domain, idev->igroup->group);
idev->igroup->hwpt = NULL;
}
iopt_remove_reserved_iova(&hwpt->ioas->iopt, idev->dev); iopt_remove_reserved_iova(&hwpt->ioas->iopt, idev->dev);
} mutex_unlock(&idev->igroup->lock);
static int iommufd_device_do_attach(struct iommufd_device *idev,
struct iommufd_hw_pagetable *hwpt)
{
int rc;
mutex_lock(&hwpt->devices_lock);
rc = iommufd_hw_pagetable_attach(hwpt, idev);
if (rc)
goto out_unlock;
idev->hwpt = hwpt; /* Caller must destroy hwpt */
refcount_inc(&hwpt->obj.users); return hwpt;
list_add(&idev->devices_item, &hwpt->devices);
out_unlock:
mutex_unlock(&hwpt->devices_lock);
return rc;
} }
/* /*
...@@ -418,7 +414,7 @@ static int iommufd_device_do_attach(struct iommufd_device *idev, ...@@ -418,7 +414,7 @@ static int iommufd_device_do_attach(struct iommufd_device *idev,
* Automatic domain selection will never pick a manually created domain. * Automatic domain selection will never pick a manually created domain.
*/ */
static int iommufd_device_auto_get_domain(struct iommufd_device *idev, static int iommufd_device_auto_get_domain(struct iommufd_device *idev,
struct iommufd_ioas *ioas) struct iommufd_ioas *ioas, u32 *pt_id)
{ {
struct iommufd_hw_pagetable *hwpt; struct iommufd_hw_pagetable *hwpt;
int rc; int rc;
...@@ -435,7 +431,7 @@ static int iommufd_device_auto_get_domain(struct iommufd_device *idev, ...@@ -435,7 +431,7 @@ static int iommufd_device_auto_get_domain(struct iommufd_device *idev,
if (!iommufd_lock_obj(&hwpt->obj)) if (!iommufd_lock_obj(&hwpt->obj))
continue; continue;
rc = iommufd_device_do_attach(idev, hwpt); rc = iommufd_hw_pagetable_attach(hwpt, idev);
iommufd_put_object(&hwpt->obj); iommufd_put_object(&hwpt->obj);
/* /*
...@@ -445,6 +441,7 @@ static int iommufd_device_auto_get_domain(struct iommufd_device *idev, ...@@ -445,6 +441,7 @@ static int iommufd_device_auto_get_domain(struct iommufd_device *idev,
*/ */
if (rc == -EINVAL) if (rc == -EINVAL)
continue; continue;
*pt_id = hwpt->obj.id;
goto out_unlock; goto out_unlock;
} }
...@@ -454,6 +451,7 @@ static int iommufd_device_auto_get_domain(struct iommufd_device *idev, ...@@ -454,6 +451,7 @@ static int iommufd_device_auto_get_domain(struct iommufd_device *idev,
goto out_unlock; goto out_unlock;
} }
hwpt->auto_domain = true; hwpt->auto_domain = true;
*pt_id = hwpt->obj.id;
mutex_unlock(&ioas->mutex); mutex_unlock(&ioas->mutex);
iommufd_object_finalize(idev->ictx, &hwpt->obj); iommufd_object_finalize(idev->ictx, &hwpt->obj);
...@@ -489,7 +487,7 @@ int iommufd_device_attach(struct iommufd_device *idev, u32 *pt_id) ...@@ -489,7 +487,7 @@ int iommufd_device_attach(struct iommufd_device *idev, u32 *pt_id)
struct iommufd_hw_pagetable *hwpt = struct iommufd_hw_pagetable *hwpt =
container_of(pt_obj, struct iommufd_hw_pagetable, obj); container_of(pt_obj, struct iommufd_hw_pagetable, obj);
rc = iommufd_device_do_attach(idev, hwpt); rc = iommufd_hw_pagetable_attach(hwpt, idev);
if (rc) if (rc)
goto out_put_pt_obj; goto out_put_pt_obj;
break; break;
...@@ -498,7 +496,7 @@ int iommufd_device_attach(struct iommufd_device *idev, u32 *pt_id) ...@@ -498,7 +496,7 @@ int iommufd_device_attach(struct iommufd_device *idev, u32 *pt_id)
struct iommufd_ioas *ioas = struct iommufd_ioas *ioas =
container_of(pt_obj, struct iommufd_ioas, obj); container_of(pt_obj, struct iommufd_ioas, obj);
rc = iommufd_device_auto_get_domain(idev, ioas); rc = iommufd_device_auto_get_domain(idev, ioas, pt_id);
if (rc) if (rc)
goto out_put_pt_obj; goto out_put_pt_obj;
break; break;
...@@ -509,7 +507,6 @@ int iommufd_device_attach(struct iommufd_device *idev, u32 *pt_id) ...@@ -509,7 +507,6 @@ int iommufd_device_attach(struct iommufd_device *idev, u32 *pt_id)
} }
refcount_inc(&idev->obj.users); refcount_inc(&idev->obj.users);
*pt_id = idev->hwpt->obj.id;
rc = 0; rc = 0;
out_put_pt_obj: out_put_pt_obj:
...@@ -527,14 +524,9 @@ EXPORT_SYMBOL_NS_GPL(iommufd_device_attach, IOMMUFD); ...@@ -527,14 +524,9 @@ EXPORT_SYMBOL_NS_GPL(iommufd_device_attach, IOMMUFD);
*/ */
void iommufd_device_detach(struct iommufd_device *idev) void iommufd_device_detach(struct iommufd_device *idev)
{ {
struct iommufd_hw_pagetable *hwpt = idev->hwpt; struct iommufd_hw_pagetable *hwpt;
mutex_lock(&hwpt->devices_lock);
list_del(&idev->devices_item);
idev->hwpt = NULL;
iommufd_hw_pagetable_detach(hwpt, idev);
mutex_unlock(&hwpt->devices_lock);
hwpt = iommufd_hw_pagetable_detach(idev);
if (hwpt->auto_domain) if (hwpt->auto_domain)
iommufd_object_destroy_user(idev->ictx, &hwpt->obj); iommufd_object_destroy_user(idev->ictx, &hwpt->obj);
else else
......
...@@ -11,8 +11,6 @@ void iommufd_hw_pagetable_destroy(struct iommufd_object *obj) ...@@ -11,8 +11,6 @@ void iommufd_hw_pagetable_destroy(struct iommufd_object *obj)
struct iommufd_hw_pagetable *hwpt = struct iommufd_hw_pagetable *hwpt =
container_of(obj, struct iommufd_hw_pagetable, obj); container_of(obj, struct iommufd_hw_pagetable, obj);
WARN_ON(!list_empty(&hwpt->devices));
if (!list_empty(&hwpt->hwpt_item)) { if (!list_empty(&hwpt->hwpt_item)) {
mutex_lock(&hwpt->ioas->mutex); mutex_lock(&hwpt->ioas->mutex);
list_del(&hwpt->hwpt_item); list_del(&hwpt->hwpt_item);
...@@ -25,7 +23,6 @@ void iommufd_hw_pagetable_destroy(struct iommufd_object *obj) ...@@ -25,7 +23,6 @@ void iommufd_hw_pagetable_destroy(struct iommufd_object *obj)
iommu_domain_free(hwpt->domain); iommu_domain_free(hwpt->domain);
refcount_dec(&hwpt->ioas->obj.users); refcount_dec(&hwpt->ioas->obj.users);
mutex_destroy(&hwpt->devices_lock);
} }
/** /**
...@@ -52,9 +49,7 @@ iommufd_hw_pagetable_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas, ...@@ -52,9 +49,7 @@ iommufd_hw_pagetable_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas,
if (IS_ERR(hwpt)) if (IS_ERR(hwpt))
return hwpt; return hwpt;
INIT_LIST_HEAD(&hwpt->devices);
INIT_LIST_HEAD(&hwpt->hwpt_item); INIT_LIST_HEAD(&hwpt->hwpt_item);
mutex_init(&hwpt->devices_lock);
/* Pairs with iommufd_hw_pagetable_destroy() */ /* Pairs with iommufd_hw_pagetable_destroy() */
refcount_inc(&ioas->obj.users); refcount_inc(&ioas->obj.users);
hwpt->ioas = ioas; hwpt->ioas = ioas;
...@@ -65,8 +60,6 @@ iommufd_hw_pagetable_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas, ...@@ -65,8 +60,6 @@ iommufd_hw_pagetable_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas,
goto out_abort; goto out_abort;
} }
mutex_lock(&hwpt->devices_lock);
/* /*
* immediate_attach exists only to accommodate iommu drivers that cannot * immediate_attach exists only to accommodate iommu drivers that cannot
* directly allocate a domain. These drivers do not finish creating the * directly allocate a domain. These drivers do not finish creating the
...@@ -76,29 +69,18 @@ iommufd_hw_pagetable_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas, ...@@ -76,29 +69,18 @@ iommufd_hw_pagetable_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas,
if (immediate_attach) { if (immediate_attach) {
rc = iommufd_hw_pagetable_attach(hwpt, idev); rc = iommufd_hw_pagetable_attach(hwpt, idev);
if (rc) if (rc)
goto out_unlock; goto out_abort;
} }
rc = iopt_table_add_domain(&hwpt->ioas->iopt, hwpt->domain); rc = iopt_table_add_domain(&hwpt->ioas->iopt, hwpt->domain);
if (rc) if (rc)
goto out_detach; goto out_detach;
list_add_tail(&hwpt->hwpt_item, &hwpt->ioas->hwpt_list); 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; return hwpt;
out_detach: out_detach:
if (immediate_attach) if (immediate_attach)
iommufd_hw_pagetable_detach(hwpt, idev); iommufd_hw_pagetable_detach(idev);
out_unlock:
mutex_unlock(&hwpt->devices_lock);
out_abort: out_abort:
iommufd_object_abort_and_destroy(ictx, &hwpt->obj); iommufd_object_abort_and_destroy(ictx, &hwpt->obj);
return ERR_PTR(rc); return ERR_PTR(rc);
......
...@@ -250,8 +250,6 @@ struct iommufd_hw_pagetable { ...@@ -250,8 +250,6 @@ struct iommufd_hw_pagetable {
bool msi_cookie : 1; bool msi_cookie : 1;
/* Head at iommufd_ioas::hwpt_list */ /* Head at iommufd_ioas::hwpt_list */
struct list_head hwpt_item; struct list_head hwpt_item;
struct mutex devices_lock;
struct list_head devices;
}; };
struct iommufd_hw_pagetable * struct iommufd_hw_pagetable *
...@@ -259,14 +257,17 @@ iommufd_hw_pagetable_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas, ...@@ -259,14 +257,17 @@ iommufd_hw_pagetable_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas,
struct iommufd_device *idev, bool immediate_attach); struct iommufd_device *idev, bool immediate_attach);
int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt, int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
struct iommufd_device *idev); struct iommufd_device *idev);
void iommufd_hw_pagetable_detach(struct iommufd_hw_pagetable *hwpt, struct iommufd_hw_pagetable *
struct iommufd_device *idev); iommufd_hw_pagetable_detach(struct iommufd_device *idev);
void iommufd_hw_pagetable_destroy(struct iommufd_object *obj); void iommufd_hw_pagetable_destroy(struct iommufd_object *obj);
struct iommufd_group { struct iommufd_group {
struct kref ref; struct kref ref;
struct mutex lock;
struct iommufd_ctx *ictx; struct iommufd_ctx *ictx;
struct iommu_group *group; struct iommu_group *group;
struct iommufd_hw_pagetable *hwpt;
struct list_head device_list;
}; };
/* /*
...@@ -278,9 +279,7 @@ struct iommufd_device { ...@@ -278,9 +279,7 @@ struct iommufd_device {
struct iommufd_object obj; struct iommufd_object obj;
struct iommufd_ctx *ictx; struct iommufd_ctx *ictx;
struct iommufd_group *igroup; struct iommufd_group *igroup;
struct iommufd_hw_pagetable *hwpt; struct list_head group_item;
/* Head at iommufd_hw_pagetable::devices */
struct list_head devices_item;
/* always the physical device */ /* always the physical device */
struct device *dev; struct device *dev;
bool enforce_cache_coherency; bool enforce_cache_coherency;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment