summaryrefslogtreecommitdiff
path: root/drivers/iommu/amd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iommu/amd')
-rw-r--r--drivers/iommu/amd/init.c6
-rw-r--r--drivers/iommu/amd/iommu.c22
-rw-r--r--drivers/iommu/amd/nested.c34
3 files changed, 41 insertions, 21 deletions
diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c
index e93bcb5eef70..e7d7b4cb9337 100644
--- a/drivers/iommu/amd/init.c
+++ b/drivers/iommu/amd/init.c
@@ -3864,6 +3864,12 @@ not_found:
return 1;
found:
+ if (early_acpihid_map_size == EARLY_MAP_SIZE) {
+ pr_err("Early ACPI HID map overflow - ignoring ivrs_acpihid%s\n",
+ str);
+ return 1;
+ }
+
p = acpiid;
hid = strsep(&p, ":");
uid = p;
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 563f9c2672d5..29dc18d3d22e 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -1450,11 +1450,23 @@ static int iommu_completion_wait(struct amd_iommu *iommu)
int ret;
u64 data;
- if (!iommu->need_sync)
- return 0;
-
raw_spin_lock_irqsave(&iommu->lock, flags);
+ if (!iommu->need_sync) {
+ /*
+ * No command has been queued since the last completion-wait.
+ * A concurrent CPU may have already queued that CWAIT and
+ * cleared need_sync; need_sync == false only means a covering
+ * CWAIT is queued, not that all prior commands have completed.
+ * Wait for the last allocated sequence number so that any
+ * command queued before this call (possibly on another CPU)
+ * is guaranteed to have completed before returning.
+ */
+ data = iommu->cmd_sem_val;
+ raw_spin_unlock_irqrestore(&iommu->lock, flags);
+ return wait_on_sem(iommu, data);
+ }
+
data = get_cmdsem_val(iommu);
build_completion_wait(&cmd, iommu, data);
@@ -1464,9 +1476,7 @@ static int iommu_completion_wait(struct amd_iommu *iommu)
if (ret)
return ret;
- ret = wait_on_sem(iommu, data);
-
- return ret;
+ return wait_on_sem(iommu, data);
}
static void domain_flush_complete(struct protection_domain *domain)
diff --git a/drivers/iommu/amd/nested.c b/drivers/iommu/amd/nested.c
index 5b902598e68a..63b53b29e029 100644
--- a/drivers/iommu/amd/nested.c
+++ b/drivers/iommu/amd/nested.c
@@ -59,7 +59,9 @@ static int validate_gdte_nested(struct iommu_hwpt_amd_guest *gdte)
return 0;
}
-static void *gdom_info_load_or_alloc_locked(struct xarray *xa, unsigned long index)
+static void *gdom_info_load_or_alloc_locked(struct xarray *xa,
+ unsigned long index,
+ unsigned long *flags)
{
struct guest_domain_mapping_info *elm, *res;
@@ -67,13 +69,13 @@ static void *gdom_info_load_or_alloc_locked(struct xarray *xa, unsigned long ind
if (elm)
return elm;
- xa_unlock(xa);
+ xa_unlock_irqrestore(xa, *flags);
elm = kzalloc_obj(struct guest_domain_mapping_info);
- xa_lock(xa);
+ xa_lock_irqsave(xa, *flags);
if (!elm)
return ERR_PTR(-ENOMEM);
- res = __xa_cmpxchg(xa, index, NULL, elm, GFP_KERNEL);
+ res = __xa_cmpxchg(xa, index, NULL, elm, GFP_ATOMIC);
if (xa_is_err(res))
res = ERR_PTR(xa_err(res));
@@ -95,6 +97,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
const struct iommu_user_data *user_data)
{
int ret;
+ unsigned long irqflags;
struct nested_domain *ndom;
struct guest_domain_mapping_info *gdom_info;
struct amd_iommu_viommu *aviommu = container_of(viommu, struct amd_iommu_viommu, core);
@@ -136,11 +139,12 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
* keep track of the gDomID mapping. When the S2 is changed, the INVALIDATE_IOMMU_PAGES
* command must be issued for each hDomID in the xarray.
*/
- xa_lock(&aviommu->gdomid_array);
+ xa_lock_irqsave(&aviommu->gdomid_array, irqflags);
- gdom_info = gdom_info_load_or_alloc_locked(&aviommu->gdomid_array, ndom->gdom_id);
+ gdom_info = gdom_info_load_or_alloc_locked(&aviommu->gdomid_array,
+ ndom->gdom_id, &irqflags);
if (IS_ERR(gdom_info)) {
- xa_unlock(&aviommu->gdomid_array);
+ xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
ret = PTR_ERR(gdom_info);
goto out_err;
}
@@ -148,7 +152,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
/* Check if gDomID exist */
if (refcount_inc_not_zero(&gdom_info->users)) {
ndom->gdom_info = gdom_info;
- xa_unlock(&aviommu->gdomid_array);
+ xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
pr_debug("%s: Found gdom_id=%#x, hdom_id=%#x\n",
__func__, ndom->gdom_id, gdom_info->hdom_id);
@@ -161,7 +165,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
if (gdom_info->hdom_id <= 0) {
__xa_cmpxchg(&aviommu->gdomid_array,
ndom->gdom_id, gdom_info, NULL, GFP_ATOMIC);
- xa_unlock(&aviommu->gdomid_array);
+ xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
ret = -ENOSPC;
goto out_err_gdom_info;
}
@@ -169,7 +173,7 @@ amd_iommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
ndom->gdom_info = gdom_info;
refcount_set(&gdom_info->users, 1);
- xa_unlock(&aviommu->gdomid_array);
+ xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
pr_debug("%s: Allocate gdom_id=%#x, hdom_id=%#x\n",
__func__, ndom->gdom_id, gdom_info->hdom_id);
@@ -257,14 +261,15 @@ static int nested_attach_device(struct iommu_domain *dom, struct device *dev,
static void nested_domain_free(struct iommu_domain *dom)
{
+ unsigned long irqflags;
struct guest_domain_mapping_info *curr;
- struct nested_domain *ndom = to_ndomain(dom);
+ struct nested_domain *ndom __free(kfree) = to_ndomain(dom);
struct amd_iommu_viommu *aviommu = ndom->viommu;
- xa_lock(&aviommu->gdomid_array);
+ xa_lock_irqsave(&aviommu->gdomid_array, irqflags);
if (!refcount_dec_and_test(&ndom->gdom_info->users)) {
- xa_unlock(&aviommu->gdomid_array);
+ xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
return;
}
@@ -275,7 +280,7 @@ static void nested_domain_free(struct iommu_domain *dom)
curr = __xa_cmpxchg(&aviommu->gdomid_array, ndom->gdom_id,
ndom->gdom_info, NULL, GFP_ATOMIC);
- xa_unlock(&aviommu->gdomid_array);
+ xa_unlock_irqrestore(&aviommu->gdomid_array, irqflags);
if (WARN_ON(!curr || xa_err(curr)))
return;
@@ -285,7 +290,6 @@ static void nested_domain_free(struct iommu_domain *dom)
amd_iommu_pdom_id_free(ndom->gdom_info->hdom_id);
kfree(curr);
- kfree(ndom);
}
static const struct iommu_domain_ops nested_domain_ops = {