Re: [PATCH 1/1] iommu/vt-d: Fix RID2PASID setup failure

From: Baolu Lu
Date: Tue Jun 21 2022 - 05:03:49 EST


On 2022/6/21 13:48, Tian, Kevin wrote:
From: Baolu Lu <baolu.lu@xxxxxxxxxxxxxxx>
Sent: Tuesday, June 21, 2022 12:28 PM

On 2022/6/21 11:46, Tian, Kevin wrote:
From: Baolu Lu <baolu.lu@xxxxxxxxxxxxxxx>
Sent: Tuesday, June 21, 2022 11:39 AM

On 2022/6/21 10:54, Tian, Kevin wrote:
From: Lu Baolu <baolu.lu@xxxxxxxxxxxxxxx>
Sent: Monday, June 20, 2022 4:17 PM
@@ -2564,7 +2564,7 @@ static int domain_add_dev_info(struct
dmar_domain *domain, struct device *dev)
ret = intel_pasid_setup_second_level(iommu,
domain,
dev, PASID_RID2PASID);
spin_unlock_irqrestore(&iommu->lock, flags);
- if (ret) {
+ if (ret && ret != -EBUSY) {
dev_err(dev, "Setup RID2PASID failed\n");
dmar_remove_one_dev_info(dev);
return ret;
--
2.25.1

It's cleaner to avoid this error at the first place, i.e. only do the
setup when the first device is attached to the pasid table.

The logic that identifies the first device might introduce additional
unnecessary complexity. Devices that share a pasid table are rare. I
even prefer to give up sharing tables so that the code can be
simpler.:-)


It's not that complex if you simply move device_attach_pasid_table()
out of intel_pasid_alloc_table(). Then do the setup if
list_empty(&pasid_table->dev) and then attach device to the
pasid table in domain_add_dev_info().

The pasid table is part of the device, hence a better place to
allocate/free the pasid table is in the device probe/release paths.
Things will become more complicated if we change relationship between
device and it's pasid table when attaching/detaching a domain. That's
the reason why I thought it was additional complexity.


If you do want to follow current route it’s still cleaner to check
whether the pasid entry has pointed to the domain in the individual
setup function instead of blindly returning -EBUSY and then ignoring
it even if a real busy condition occurs. The setup functions can
just return zero for this benign alias case.

Kevin, how do you like this one?

diff --git a/drivers/iommu/intel/pasid.c b/drivers/iommu/intel/pasid.c
index cb4c1d0cf25c..ecffd0129b2b 100644
--- a/drivers/iommu/intel/pasid.c
+++ b/drivers/iommu/intel/pasid.c
@@ -575,6 +575,16 @@ static inline int pasid_enable_wpe(struct pasid_entry *pte)
return 0;
};

+/*
+ * Return true if @pasid is RID2PASID and the domain @did has already
+ * been setup to the @pte. Otherwise, return false.
+ */
+static inline bool
+rid2pasid_domain_valid(struct pasid_entry *pte, u32 pasid, u16 did)
+{
+ return pasid == PASID_RID2PASID && pasid_get_domain_id(pte) == did;
+}
+
/*
* Set up the scalable mode pasid table entry for first only
* translation type.
@@ -595,9 +605,8 @@ int intel_pasid_setup_first_level(struct intel_iommu *iommu,
if (WARN_ON(!pte))
return -EINVAL;

- /* Caller must ensure PASID entry is not in use. */
if (pasid_pte_is_present(pte))
- return -EBUSY;
+ return rid2pasid_domain_valid(pte, pasid, did) ? 0: -EBUSY;

pasid_clear_entry(pte);

@@ -698,9 +707,8 @@ int intel_pasid_setup_second_level(struct intel_iommu *iommu,
return -ENODEV;
}

- /* Caller must ensure PASID entry is not in use. */
if (pasid_pte_is_present(pte))
- return -EBUSY;
+ return rid2pasid_domain_valid(pte, pasid, did) ? 0: -EBUSY;

pasid_clear_entry(pte);
pasid_set_domain_id(pte, did);
@@ -738,9 +746,8 @@ int intel_pasid_setup_pass_through(struct intel_iommu *iommu,
return -ENODEV;
}

- /* Caller must ensure PASID entry is not in use. */
if (pasid_pte_is_present(pte))
- return -EBUSY;
+ return rid2pasid_domain_valid(pte, pasid, did) ? 0: -EBUSY;

pasid_clear_entry(pte);
pasid_set_domain_id(pte, did);

--
Best regards,
baolu