@@ -67,6 +67,8 @@ static void pci_pasid_init(struct pci_dev *pdev)
if (pdev->is_virtfn)
return;
+ mutex_init(&pdev->pasid_lock);
+
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID);
if (!pos)
return;
@@ -441,32 +443,57 @@ EXPORT_SYMBOL_GPL(pci_reset_pri);
int pci_enable_pasid(struct pci_dev *pdev, int features)
{
u16 control, supported;
+ int ret = 0;
+ struct pci_dev *pf = pci_physfn(pdev);
- if (WARN_ON(pdev->pasid_enabled))
- return -EBUSY;
+ mutex_lock(&pf->pasid_lock);
- if (!pdev->eetlp_prefix_path)
- return -EINVAL;
+ if (WARN_ON(pdev->pasid_enabled)) {
+ ret = -EBUSY;
+ goto pasid_unlock;
+ }
- if (!pdev->pasid_cap)
- return -EINVAL;
+ if (!pdev->eetlp_prefix_path) {
+ ret = -EINVAL;
+ goto pasid_unlock;
+ }
- pci_read_config_word(pdev, pdev->pasid_cap + PCI_PASID_CAP,
- &supported);
+ if (!pf->pasid_cap) {
+ ret = -EINVAL;
+ goto pasid_unlock;
+ }
+
+ if (pdev->is_virtfn && pf->pasid_enabled)
+ goto update_status;
+
+ pci_read_config_word(pf, pf->pasid_cap + PCI_PASID_CAP, &supported);
supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV;
/* User wants to enable anything unsupported? */
- if ((supported & features) != features)
- return -EINVAL;
+ if ((supported & features) != features) {
+ ret = -EINVAL;
+ goto pasid_unlock;
+ }
control = PCI_PASID_CTRL_ENABLE | features;
- pdev->pasid_features = features;
-
+ pf->pasid_features = features;
pci_write_config_word(pdev, pdev->pasid_cap + PCI_PASID_CTRL, control);
- pdev->pasid_enabled = 1;
+ /*
+ * If PASID is not already enabled in PF, increment pasid_ref_cnt
+ * to count PF PASID usage.
+ */
+ if (pdev->is_virtfn && !pf->pasid_enabled) {
+ atomic_inc(&pf->pasid_ref_cnt);
+ pf->pasid_enabled = 1;
+ }
- return 0;
+update_status:
+ atomic_inc(&pf->pasid_ref_cnt);
+ pdev->pasid_enabled = 1;
+pasid_unlock:
+ mutex_unlock(&pf->pasid_lock);
+ return ret;
}
EXPORT_SYMBOL_GPL(pci_enable_pasid);
@@ -477,16 +504,29 @@ EXPORT_SYMBOL_GPL(pci_enable_pasid);
void pci_disable_pasid(struct pci_dev *pdev)
{
u16 control = 0;
+ struct pci_dev *pf = pci_physfn(pdev);
+
+ mutex_lock(&pf->pasid_lock);
if (WARN_ON(!pdev->pasid_enabled))
- return;
+ goto pasid_unlock;
- if (!pdev->pasid_cap)
- return;
+ if (!pf->pasid_cap)
+ goto pasid_unlock;
- pci_write_config_word(pdev, pdev->pasid_cap + PCI_PASID_CTRL, control);
+ atomic_dec(&pf->pasid_ref_cnt);
+ if (atomic_read(&pf->pasid_ref_cnt))
+ goto done;
+
+ /* Disable PASID only if pasid_ref_cnt is zero */
+ pci_write_config_word(pf, pf->pasid_cap + PCI_PASID_CTRL, control);
+
+done:
pdev->pasid_enabled = 0;
+pasid_unlock:
+ mutex_unlock(&pf->pasid_lock);
+
}
EXPORT_SYMBOL_GPL(pci_disable_pasid);
@@ -497,15 +537,25 @@ EXPORT_SYMBOL_GPL(pci_disable_pasid);
void pci_restore_pasid_state(struct pci_dev *pdev)
{
u16 control;
+ struct pci_dev *pf = pci_physfn(pdev);
if (!pdev->pasid_enabled)
return;
- if (!pdev->pasid_cap)
+ if (!pf->pasid_cap)
return;
+ mutex_lock(&pf->pasid_lock);
+
+ pci_read_config_word(pf, pf->pasid_cap + PCI_PASID_CTRL, &control);
+ if (control & PCI_PASID_CTRL_ENABLE)
+ goto pasid_unlock;
+
control = PCI_PASID_CTRL_ENABLE | pdev->pasid_features;
- pci_write_config_word(pdev, pdev->pasid_cap + PCI_PASID_CTRL, control);
+ pci_write_config_word(pf, pf->pasid_cap + PCI_PASID_CTRL, control);
+
+pasid_unlock:
+ mutex_unlock(&pf->pasid_lock);
}
EXPORT_SYMBOL_GPL(pci_restore_pasid_state);
@@ -522,15 +572,22 @@ EXPORT_SYMBOL_GPL(pci_restore_pasid_state);
int pci_pasid_features(struct pci_dev *pdev)
{
u16 supported;
+ struct pci_dev *pf = pci_physfn(pdev);
+
+ mutex_lock(&pf->pasid_lock);
- if (!pdev->pasid_cap)
+ if (!pf->pasid_cap) {
+ mutex_unlock(&pf->pasid_lock);
return -EINVAL;
+ }
- pci_read_config_word(pdev, pdev->pasid_cap + PCI_PASID_CAP,
+ pci_read_config_word(pf, pf->pasid_cap + PCI_PASID_CAP,
&supported);
supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV;
+ mutex_unlock(&pf->pasid_lock);
+
return supported;
}
EXPORT_SYMBOL_GPL(pci_pasid_features);
@@ -584,15 +641,21 @@ EXPORT_SYMBOL_GPL(pci_prg_resp_pasid_required);
int pci_max_pasids(struct pci_dev *pdev)
{
u16 supported;
+ struct pci_dev *pf = pci_physfn(pdev);
+
+ mutex_lock(&pf->pasid_lock);
- if (!pdev->pasid_cap)
+ if (!pf->pasid_cap) {
+ mutex_unlock(&pf->pasid_lock);
return -EINVAL;
+ }
- pci_read_config_word(pdev, pdev->pasid_cap + PCI_PASID_CAP,
- &supported);
+ pci_read_config_word(pf, pf->pasid_cap + PCI_PASID_CAP, &supported);
supported = (supported & PASID_NUMBER_MASK) >> PASID_NUMBER_SHIFT;
+ mutex_unlock(&pf->pasid_lock);
+
return (1 << supported);
}
EXPORT_SYMBOL_GPL(pci_max_pasids);
@@ -458,8 +458,10 @@ struct pci_dev {
atomic_t pri_ref_cnt; /* Number of PF/VF PRI users */
#endif
#ifdef CONFIG_PCI_PASID
+ struct mutex pasid_lock; /* PASID enable lock */
u16 pasid_cap; /* PASID Capability offset */
u16 pasid_features;
+ atomic_t pasid_ref_cnt; /* Number of VFs with PASID enabled */
#endif
#ifdef CONFIG_PCI_P2PDMA
struct pci_p2pdma *p2pdma;