diff mbox series

[XEN,v10,12/20] xen/smmu: Add remove_device callback for smmu_iommu ops

Message ID 20230825080222.14247-13-vikram.garhwal@amd.com (mailing list archive)
State Superseded
Headers show
Series dynamic node programming using overlay dtbo | expand

Commit Message

Vikram Garhwal Aug. 25, 2023, 8:02 a.m. UTC
Add remove_device callback for removing the device entry from smmu-master using
following steps:
1. Find if SMMU master exists for the device node.
2. Check if device is currently in use.
3. Remove the SMMU master.

Signed-off-by: Vikram Garhwal <vikram.garhwal@amd.com>

---
 Changes from v9:
    Remove iommu_dt_device_is_assigned_locked().
    Add comment regarding caller holding the locks to protect concurrent access.
 Changes from v7:
        Added comments on arm_smmu_dt_remove_device_generic().
---
---
 xen/drivers/passthrough/arm/smmu.c | 60 ++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

Comments

Michal Orzel Aug. 29, 2023, 8:51 a.m. UTC | #1
On 25/08/2023 10:02, Vikram Garhwal wrote:
> 
> 
> Add remove_device callback for removing the device entry from smmu-master using
> following steps:
> 1. Find if SMMU master exists for the device node.
> 2. Check if device is currently in use.
Since you removed a call to iommu_dt_device_is_assigned_locked(), you do not check it from SMMU, right?
You are relying on a check done in iommu_remove_dt_device().
This wants to be mentioned. However, Julien suggested to do the check for internal SMMU state.
Looking at the code, when the device is assigned, we do:
dev_iommu_domain(dev) = domain;
and when de-assigned:
dev_iommu_domain(dev) = NULL;

This means that before calling remove_smmu_master() you could do:

/* Make sure device is not assigned */
if (dev_iommu_domain(dev))
    return -EBUSY;

@Julien, @Stefano?

~Michal
Stefano Stabellini Aug. 29, 2023, 10:45 p.m. UTC | #2
On Tue, 29 Aug 2023, Michal Orzel wrote:
> On 25/08/2023 10:02, Vikram Garhwal wrote:
> > Add remove_device callback for removing the device entry from smmu-master using
> > following steps:
> > 1. Find if SMMU master exists for the device node.
> > 2. Check if device is currently in use.
> Since you removed a call to iommu_dt_device_is_assigned_locked(), you do not check it from SMMU, right?
> You are relying on a check done in iommu_remove_dt_device().
> This wants to be mentioned. However, Julien suggested to do the check for internal SMMU state.
> Looking at the code, when the device is assigned, we do:
> dev_iommu_domain(dev) = domain;
> and when de-assigned:
> dev_iommu_domain(dev) = NULL;
> 
> This means that before calling remove_smmu_master() you could do:
> 
> /* Make sure device is not assigned */
> if (dev_iommu_domain(dev))
>     return -EBUSY;
> 
> @Julien, @Stefano?

I think it is OK without it, as we have a call to
iommu_dt_device_is_assigned_locked(np) already in
iommu_remove_dt_device?
Michal Orzel Aug. 30, 2023, 9:17 a.m. UTC | #3
On 30/08/2023 00:45, Stefano Stabellini wrote:
> 
> 
> On Tue, 29 Aug 2023, Michal Orzel wrote:
>> On 25/08/2023 10:02, Vikram Garhwal wrote:
>>> Add remove_device callback for removing the device entry from smmu-master using
>>> following steps:
>>> 1. Find if SMMU master exists for the device node.
>>> 2. Check if device is currently in use.
>> Since you removed a call to iommu_dt_device_is_assigned_locked(), you do not check it from SMMU, right?
>> You are relying on a check done in iommu_remove_dt_device().
>> This wants to be mentioned. However, Julien suggested to do the check for internal SMMU state.
>> Looking at the code, when the device is assigned, we do:
>> dev_iommu_domain(dev) = domain;
>> and when de-assigned:
>> dev_iommu_domain(dev) = NULL;
>>
>> This means that before calling remove_smmu_master() you could do:
>>
>> /* Make sure device is not assigned */
>> if (dev_iommu_domain(dev))
>>     return -EBUSY;
>>
>> @Julien, @Stefano?
> 
> I think it is OK without it, as we have a call to
> iommu_dt_device_is_assigned_locked(np) already in
> iommu_remove_dt_device?

Yes, but this would add an extra layer of protection by checking for IOMMU domain for this device.

~Michal
diff mbox series

Patch

diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
index c37fa9af13..71799064f8 100644
--- a/xen/drivers/passthrough/arm/smmu.c
+++ b/xen/drivers/passthrough/arm/smmu.c
@@ -815,6 +815,19 @@  static int insert_smmu_master(struct arm_smmu_device *smmu,
 	return 0;
 }
 
+static int remove_smmu_master(struct arm_smmu_device *smmu,
+			      struct arm_smmu_master *master)
+{
+	if (!smmu->masters.rb_node) {
+		ASSERT_UNREACHABLE();
+		return -ENOENT;
+	}
+
+	rb_erase(&master->node, &smmu->masters);
+
+	return 0;
+}
+
 static int arm_smmu_dt_add_device_legacy(struct arm_smmu_device *smmu,
 					 struct device *dev,
 					 struct iommu_fwspec *fwspec)
@@ -852,6 +865,32 @@  static int arm_smmu_dt_add_device_legacy(struct arm_smmu_device *smmu,
 	return insert_smmu_master(smmu, master);
 }
 
+static int arm_smmu_dt_remove_device_legacy(struct arm_smmu_device *smmu,
+					 struct device *dev)
+{
+	struct arm_smmu_master *master;
+	struct device_node *dev_node = dev_get_dev_node(dev);
+	int ret;
+
+	master = find_smmu_master(smmu, dev_node);
+	if (master == NULL) {
+		dev_err(dev,
+			"No registrations found for master device %s\n",
+			dev_node->name);
+		return -EINVAL;
+	}
+
+	ret = remove_smmu_master(smmu, master);
+	if (ret)
+		return ret;
+
+	/* Protected by dt_host_lock and dtdevs_lock as caller holds these locks. */
+	dev_node->is_protected = false;
+
+	kfree(master);
+	return 0;
+}
+
 static int register_smmu_master(struct arm_smmu_device *smmu,
 				struct device *dev,
 				struct of_phandle_args *masterspec)
@@ -875,6 +914,26 @@  static int register_smmu_master(struct arm_smmu_device *smmu,
 					     fwspec);
 }
 
+/*
+ * The driver which supports generic IOMMU DT bindings must have this
+ * callback implemented.
+ */
+static int arm_smmu_dt_remove_device_generic(u8 devfn, struct device *dev)
+{
+	struct arm_smmu_device *smmu;
+	struct iommu_fwspec *fwspec;
+
+	fwspec = dev_iommu_fwspec_get(dev);
+	if (fwspec == NULL)
+		return -ENXIO;
+
+	smmu = find_smmu(fwspec->iommu_dev);
+	if (smmu == NULL)
+		return -ENXIO;
+
+	return arm_smmu_dt_remove_device_legacy(smmu, dev);
+}
+
 static int arm_smmu_dt_add_device_generic(u8 devfn, struct device *dev)
 {
 	struct arm_smmu_device *smmu;
@@ -2859,6 +2918,7 @@  static const struct iommu_ops arm_smmu_iommu_ops = {
     .init = arm_smmu_iommu_domain_init,
     .hwdom_init = arch_iommu_hwdom_init,
     .add_device = arm_smmu_dt_add_device_generic,
+    .remove_device = arm_smmu_dt_remove_device_generic,
     .teardown = arm_smmu_iommu_domain_teardown,
     .iotlb_flush = arm_smmu_iotlb_flush,
     .assign_device = arm_smmu_assign_dev,