mirror of
https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9.git
synced 2026-09-09 00:08:12 +08:00
Merge: [RHEL9.9] Updates to the IOMMU and DMA Mapping API subsystems
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/8007 # Merge Request Required Information JIRA: https://issues.redhat.com/browse/RHEL-147906 Upstream-Status: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git ## Summary of Changes Various fixes touching the subsystems that have landed upstream recently. Clean cherry-picks except for 1 commit that removed a function that has a different name in RHEL9. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Jerry Snitselaar <jsnitsel@redhat.com> Approved-by: Eder Zulian <ezulian@redhat.com> Approved-by: Brian Masney <bmasney@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: CKI GitLab Kmaint Pipeline Bot <26919896-cki-kmaint-pipeline-bot@users.noreply.gitlab.com>
This commit is contained in:
@@ -15,7 +15,6 @@ irqreturn_t amd_iommu_int_thread(int irq, void *data);
|
||||
irqreturn_t amd_iommu_int_thread_evtlog(int irq, void *data);
|
||||
irqreturn_t amd_iommu_int_thread_pprlog(int irq, void *data);
|
||||
irqreturn_t amd_iommu_int_thread_galog(int irq, void *data);
|
||||
irqreturn_t amd_iommu_int_handler(int irq, void *data);
|
||||
void amd_iommu_restart_log(struct amd_iommu *iommu, const char *evt_type,
|
||||
u8 cntrl_intr, u8 cntrl_log,
|
||||
u32 status_run_mask, u32 status_overflow_mask);
|
||||
|
||||
+16
-11
@@ -1608,13 +1608,22 @@ static struct amd_iommu_pci_seg *__init alloc_pci_segment(u16 id,
|
||||
list_add_tail(&pci_seg->list, &amd_iommu_pci_seg_list);
|
||||
|
||||
if (alloc_dev_table(pci_seg))
|
||||
return NULL;
|
||||
goto err_free_pci_seg;
|
||||
if (alloc_alias_table(pci_seg))
|
||||
return NULL;
|
||||
goto err_free_dev_table;
|
||||
if (alloc_rlookup_table(pci_seg))
|
||||
return NULL;
|
||||
goto err_free_alias_table;
|
||||
|
||||
return pci_seg;
|
||||
|
||||
err_free_alias_table:
|
||||
free_alias_table(pci_seg);
|
||||
err_free_dev_table:
|
||||
free_dev_table(pci_seg);
|
||||
err_free_pci_seg:
|
||||
list_del(&pci_seg->list);
|
||||
kfree(pci_seg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct amd_iommu_pci_seg *__init get_pci_segment(u16 id,
|
||||
@@ -2230,12 +2239,8 @@ static int iommu_setup_msi(struct amd_iommu *iommu)
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = request_threaded_irq(iommu->dev->irq,
|
||||
amd_iommu_int_handler,
|
||||
amd_iommu_int_thread,
|
||||
0, "AMD-Vi",
|
||||
iommu);
|
||||
|
||||
r = request_threaded_irq(iommu->dev->irq, NULL, amd_iommu_int_thread,
|
||||
IRQF_ONESHOT, "AMD-Vi", iommu);
|
||||
if (r) {
|
||||
pci_disable_msi(iommu->dev);
|
||||
return r;
|
||||
@@ -2409,8 +2414,8 @@ static int __iommu_setup_intcapxt(struct amd_iommu *iommu, const char *devname,
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = request_threaded_irq(irq, amd_iommu_int_handler,
|
||||
thread_fn, 0, devname, iommu);
|
||||
ret = request_threaded_irq(irq, NULL, thread_fn, IRQF_ONESHOT, devname,
|
||||
iommu);
|
||||
if (ret) {
|
||||
irq_domain_free_irqs(irq, 1);
|
||||
irq_domain_remove(domain);
|
||||
|
||||
@@ -1144,11 +1144,6 @@ irqreturn_t amd_iommu_int_thread(int irq, void *data)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
irqreturn_t amd_iommu_int_handler(int irq, void *data)
|
||||
{
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* IOMMU command queuing functions
|
||||
@@ -2424,8 +2419,6 @@ static struct iommu_device *amd_iommu_probe_device(struct device *dev)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
out_err:
|
||||
|
||||
iommu_completion_wait(iommu);
|
||||
|
||||
if (FEATURE_NUM_INT_REMAP_SUP_2K(amd_iommu_efr2))
|
||||
@@ -2436,6 +2429,7 @@ out_err:
|
||||
if (dev_is_pci(dev))
|
||||
pci_prepare_ats(to_pci_dev(dev), PAGE_SHIFT);
|
||||
|
||||
out_err:
|
||||
return iommu_dev;
|
||||
}
|
||||
|
||||
@@ -2666,8 +2660,20 @@ static struct iommu_domain blocked_domain = {
|
||||
|
||||
static struct protection_domain identity_domain;
|
||||
|
||||
static int amd_iommu_identity_attach(struct iommu_domain *dom, struct device *dev)
|
||||
{
|
||||
/*
|
||||
* Don't allow attaching a device to the identity domain if SNP is
|
||||
* enabled.
|
||||
*/
|
||||
if (amd_iommu_snp_en)
|
||||
return -EINVAL;
|
||||
|
||||
return amd_iommu_attach_device(dom, dev);
|
||||
}
|
||||
|
||||
static const struct iommu_domain_ops identity_domain_ops = {
|
||||
.attach_dev = amd_iommu_attach_device,
|
||||
.attach_dev = amd_iommu_identity_attach,
|
||||
};
|
||||
|
||||
void amd_iommu_init_identity_domain(void)
|
||||
@@ -3352,7 +3358,7 @@ static int __modify_irte_ga(struct amd_iommu *iommu, u16 devid, int index,
|
||||
static int modify_irte_ga(struct amd_iommu *iommu, u16 devid, int index,
|
||||
struct irte_ga *irte)
|
||||
{
|
||||
bool ret;
|
||||
int ret;
|
||||
|
||||
ret = __modify_irte_ga(iommu, devid, index, irte);
|
||||
if (ret)
|
||||
|
||||
@@ -165,7 +165,9 @@ static int arm_smmu_attach_dev_nested(struct iommu_domain *domain,
|
||||
* config bit here base this off the EATS value in the STE. If the EATS
|
||||
* is set then the VM must generate ATC flushes.
|
||||
*/
|
||||
state.disable_ats = !nested_domain->enable_ats;
|
||||
if (FIELD_GET(STRTAB_STE_0_CFG, le64_to_cpu(nested_domain->ste[0])) ==
|
||||
STRTAB_STE_0_CFG_S1_TRANS)
|
||||
state.disable_ats = !nested_domain->enable_ats;
|
||||
ret = arm_smmu_attach_prepare(&state, domain);
|
||||
if (ret) {
|
||||
mutex_unlock(&arm_smmu_asid_lock);
|
||||
|
||||
@@ -38,13 +38,16 @@ enum arm_smmu_test_master_feat {
|
||||
static bool arm_smmu_entry_differs_in_used_bits(const __le64 *entry,
|
||||
const __le64 *used_bits,
|
||||
const __le64 *target,
|
||||
const __le64 *safe,
|
||||
unsigned int length)
|
||||
{
|
||||
bool differs = false;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
if ((entry[i] & used_bits[i]) != target[i])
|
||||
__le64 used = used_bits[i] & ~safe[i];
|
||||
|
||||
if ((entry[i] & used) != (target[i] & used))
|
||||
differs = true;
|
||||
}
|
||||
return differs;
|
||||
@@ -56,12 +59,24 @@ arm_smmu_test_writer_record_syncs(struct arm_smmu_entry_writer *writer)
|
||||
struct arm_smmu_test_writer *test_writer =
|
||||
container_of(writer, struct arm_smmu_test_writer, writer);
|
||||
__le64 *entry_used_bits;
|
||||
__le64 *safe_target;
|
||||
__le64 *safe_init;
|
||||
|
||||
entry_used_bits = kunit_kzalloc(
|
||||
test_writer->test, sizeof(*entry_used_bits) * NUM_ENTRY_QWORDS,
|
||||
GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test_writer->test, entry_used_bits);
|
||||
|
||||
safe_target = kunit_kzalloc(test_writer->test,
|
||||
sizeof(*safe_target) * NUM_ENTRY_QWORDS,
|
||||
GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test_writer->test, safe_target);
|
||||
|
||||
safe_init = kunit_kzalloc(test_writer->test,
|
||||
sizeof(*safe_init) * NUM_ENTRY_QWORDS,
|
||||
GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test_writer->test, safe_init);
|
||||
|
||||
pr_debug("STE value is now set to: ");
|
||||
print_hex_dump_debug(" ", DUMP_PREFIX_NONE, 16, 8,
|
||||
test_writer->entry,
|
||||
@@ -79,14 +94,23 @@ arm_smmu_test_writer_record_syncs(struct arm_smmu_entry_writer *writer)
|
||||
* configuration.
|
||||
*/
|
||||
writer->ops->get_used(test_writer->entry, entry_used_bits);
|
||||
if (writer->ops->get_update_safe)
|
||||
writer->ops->get_update_safe(test_writer->entry,
|
||||
test_writer->init_entry,
|
||||
safe_init);
|
||||
if (writer->ops->get_update_safe)
|
||||
writer->ops->get_update_safe(test_writer->entry,
|
||||
test_writer->target_entry,
|
||||
safe_target);
|
||||
KUNIT_EXPECT_FALSE(
|
||||
test_writer->test,
|
||||
arm_smmu_entry_differs_in_used_bits(
|
||||
test_writer->entry, entry_used_bits,
|
||||
test_writer->init_entry, NUM_ENTRY_QWORDS) &&
|
||||
test_writer->init_entry, safe_init,
|
||||
NUM_ENTRY_QWORDS) &&
|
||||
arm_smmu_entry_differs_in_used_bits(
|
||||
test_writer->entry, entry_used_bits,
|
||||
test_writer->target_entry,
|
||||
test_writer->target_entry, safe_target,
|
||||
NUM_ENTRY_QWORDS));
|
||||
}
|
||||
}
|
||||
@@ -106,6 +130,7 @@ arm_smmu_v3_test_debug_print_used_bits(struct arm_smmu_entry_writer *writer,
|
||||
static const struct arm_smmu_entry_writer_ops test_ste_ops = {
|
||||
.sync = arm_smmu_test_writer_record_syncs,
|
||||
.get_used = arm_smmu_get_ste_used,
|
||||
.get_update_safe = arm_smmu_get_ste_update_safe,
|
||||
};
|
||||
|
||||
static const struct arm_smmu_entry_writer_ops test_cd_ops = {
|
||||
|
||||
@@ -1082,6 +1082,49 @@ void arm_smmu_get_ste_used(const __le64 *ent, __le64 *used_bits)
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(arm_smmu_get_ste_used);
|
||||
|
||||
VISIBLE_IF_KUNIT
|
||||
void arm_smmu_get_ste_update_safe(const __le64 *cur, const __le64 *target,
|
||||
__le64 *safe_bits)
|
||||
{
|
||||
const __le64 eats_s1chk =
|
||||
FIELD_PREP(STRTAB_STE_1_EATS, STRTAB_STE_1_EATS_S1CHK);
|
||||
const __le64 eats_trans =
|
||||
FIELD_PREP(STRTAB_STE_1_EATS, STRTAB_STE_1_EATS_TRANS);
|
||||
|
||||
/*
|
||||
* When an STE changes EATS_TRANS, the sequencing code in the attach
|
||||
* logic already will have the PCI cap for ATS disabled. Thus at this
|
||||
* moment we can expect that the device will not generate ATS queries
|
||||
* and so we don't care about the sequencing of EATS. The purpose of
|
||||
* EATS_TRANS is to protect the system from hostile untrusted devices
|
||||
* that issue ATS when the PCI config space is disabled. However, if
|
||||
* EATS_TRANS is being changed, then we must have already trusted the
|
||||
* device as the EATS_TRANS security block is being disabled.
|
||||
*
|
||||
* Note: now the EATS_TRANS update is moved to the first entry_set().
|
||||
* Changing S2S and EATS might transiently result in S2S=1 and EATS=1
|
||||
* which is a bad STE (see "5.2 Stream Table Entry"). In such a case,
|
||||
* we can't do a hitless update. Also, it should not be added to the
|
||||
* safe bits with STRTAB_STE_1_EATS_S1CHK, because EATS=0b11 would be
|
||||
* effectively an errant 0b00 configuration.
|
||||
*/
|
||||
if (!((cur[1] | target[1]) & cpu_to_le64(eats_s1chk)) &&
|
||||
!((cur[2] | target[2]) & cpu_to_le64(STRTAB_STE_2_S2S)))
|
||||
safe_bits[1] |= cpu_to_le64(eats_trans);
|
||||
|
||||
/*
|
||||
* MEV does not meaningfully impact the operation of the HW, it only
|
||||
* changes how many fault events are generated, thus we can relax it
|
||||
* when computing the ordering. The spec notes the device can act like
|
||||
* MEV=1 anyhow:
|
||||
*
|
||||
* Note: Software must expect, and be able to deal with, coalesced
|
||||
* fault records even when MEV == 0.
|
||||
*/
|
||||
safe_bits[1] |= cpu_to_le64(STRTAB_STE_1_MEV);
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(arm_smmu_get_ste_update_safe);
|
||||
|
||||
/*
|
||||
* Figure out if we can do a hitless update of entry to become target. Returns a
|
||||
* bit mask where 1 indicates that qword needs to be set disruptively.
|
||||
@@ -1094,13 +1137,22 @@ static u8 arm_smmu_entry_qword_diff(struct arm_smmu_entry_writer *writer,
|
||||
{
|
||||
__le64 target_used[NUM_ENTRY_QWORDS] = {};
|
||||
__le64 cur_used[NUM_ENTRY_QWORDS] = {};
|
||||
__le64 safe[NUM_ENTRY_QWORDS] = {};
|
||||
u8 used_qword_diff = 0;
|
||||
unsigned int i;
|
||||
|
||||
writer->ops->get_used(entry, cur_used);
|
||||
writer->ops->get_used(target, target_used);
|
||||
if (writer->ops->get_update_safe)
|
||||
writer->ops->get_update_safe(entry, target, safe);
|
||||
|
||||
for (i = 0; i != NUM_ENTRY_QWORDS; i++) {
|
||||
/*
|
||||
* Safe is only used for bits that are used by both entries,
|
||||
* otherwise it is sequenced according to the unused entry.
|
||||
*/
|
||||
safe[i] &= target_used[i] & cur_used[i];
|
||||
|
||||
/*
|
||||
* Check that masks are up to date, the make functions are not
|
||||
* allowed to set a bit to 1 if the used function doesn't say it
|
||||
@@ -1109,6 +1161,7 @@ static u8 arm_smmu_entry_qword_diff(struct arm_smmu_entry_writer *writer,
|
||||
WARN_ON_ONCE(target[i] & ~target_used[i]);
|
||||
|
||||
/* Bits can change because they are not currently being used */
|
||||
cur_used[i] &= ~safe[i];
|
||||
unused_update[i] = (entry[i] & cur_used[i]) |
|
||||
(target[i] & ~cur_used[i]);
|
||||
/*
|
||||
@@ -1121,7 +1174,7 @@ static u8 arm_smmu_entry_qword_diff(struct arm_smmu_entry_writer *writer,
|
||||
return used_qword_diff;
|
||||
}
|
||||
|
||||
static bool entry_set(struct arm_smmu_entry_writer *writer, __le64 *entry,
|
||||
static void entry_set(struct arm_smmu_entry_writer *writer, __le64 *entry,
|
||||
const __le64 *target, unsigned int start,
|
||||
unsigned int len)
|
||||
{
|
||||
@@ -1137,7 +1190,6 @@ static bool entry_set(struct arm_smmu_entry_writer *writer, __le64 *entry,
|
||||
|
||||
if (changed)
|
||||
writer->ops->sync(writer);
|
||||
return changed;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1207,12 +1259,9 @@ void arm_smmu_write_entry(struct arm_smmu_entry_writer *writer, __le64 *entry,
|
||||
entry_set(writer, entry, target, 0, 1);
|
||||
} else {
|
||||
/*
|
||||
* No inuse bit changed. Sanity check that all unused bits are 0
|
||||
* in the entry. The target was already sanity checked by
|
||||
* compute_qword_diff().
|
||||
* No inuse bit changed, though safe bits may have changed.
|
||||
*/
|
||||
WARN_ON_ONCE(
|
||||
entry_set(writer, entry, target, 0, NUM_ENTRY_QWORDS));
|
||||
entry_set(writer, entry, target, 0, NUM_ENTRY_QWORDS);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(arm_smmu_write_entry);
|
||||
@@ -1464,7 +1513,7 @@ static int arm_smmu_alloc_cd_tables(struct arm_smmu_master *master)
|
||||
cd_table->l2.l1tab = dma_alloc_coherent(smmu->dev, l1size,
|
||||
&cd_table->cdtab_dma,
|
||||
GFP_KERNEL);
|
||||
if (!cd_table->l2.l2ptrs) {
|
||||
if (!cd_table->l2.l1tab) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_l2ptrs;
|
||||
}
|
||||
@@ -1543,6 +1592,7 @@ static void arm_smmu_ste_writer_sync_entry(struct arm_smmu_entry_writer *writer)
|
||||
static const struct arm_smmu_entry_writer_ops arm_smmu_ste_writer_ops = {
|
||||
.sync = arm_smmu_ste_writer_sync_entry,
|
||||
.get_used = arm_smmu_get_ste_used,
|
||||
.get_update_safe = arm_smmu_get_ste_update_safe,
|
||||
};
|
||||
|
||||
static void arm_smmu_write_ste(struct arm_smmu_master *master, u32 sid,
|
||||
@@ -2551,7 +2601,7 @@ static int arm_smmu_domain_finalise(struct arm_smmu_domain *smmu_domain,
|
||||
ARM_SMMU_FEAT_VAX) ? 52 : 48;
|
||||
|
||||
pgtbl_cfg.ias = min_t(unsigned long, ias, VA_BITS);
|
||||
pgtbl_cfg.oas = smmu->ias;
|
||||
pgtbl_cfg.oas = smmu->oas;
|
||||
if (enable_dirty)
|
||||
pgtbl_cfg.quirks |= IO_PGTABLE_QUIRK_ARM_HD;
|
||||
fmt = ARM_64_LPAE_S1;
|
||||
@@ -2561,7 +2611,7 @@ static int arm_smmu_domain_finalise(struct arm_smmu_domain *smmu_domain,
|
||||
case ARM_SMMU_DOMAIN_S2:
|
||||
if (enable_dirty)
|
||||
return -EOPNOTSUPP;
|
||||
pgtbl_cfg.ias = smmu->ias;
|
||||
pgtbl_cfg.ias = smmu->oas;
|
||||
pgtbl_cfg.oas = smmu->oas;
|
||||
fmt = ARM_64_LPAE_S2;
|
||||
finalise_stage_fn = arm_smmu_domain_finalise_s2;
|
||||
@@ -4393,13 +4443,7 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
|
||||
}
|
||||
|
||||
/* We only support the AArch64 table format at present */
|
||||
switch (FIELD_GET(IDR0_TTF, reg)) {
|
||||
case IDR0_TTF_AARCH32_64:
|
||||
smmu->ias = 40;
|
||||
fallthrough;
|
||||
case IDR0_TTF_AARCH64:
|
||||
break;
|
||||
default:
|
||||
if (!(FIELD_GET(IDR0_TTF, reg) & IDR0_TTF_AARCH64)) {
|
||||
dev_err(smmu->dev, "AArch64 table format not supported!\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
@@ -4509,8 +4553,6 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
|
||||
dev_warn(smmu->dev,
|
||||
"failed to set DMA mask for table walker\n");
|
||||
|
||||
smmu->ias = max(smmu->ias, smmu->oas);
|
||||
|
||||
if ((smmu->features & ARM_SMMU_FEAT_TRANS_S1) &&
|
||||
(smmu->features & ARM_SMMU_FEAT_TRANS_S2))
|
||||
smmu->features |= ARM_SMMU_FEAT_NESTING;
|
||||
@@ -4520,8 +4562,8 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
|
||||
if (arm_smmu_sva_supported(smmu))
|
||||
smmu->features |= ARM_SMMU_FEAT_SVA;
|
||||
|
||||
dev_info(smmu->dev, "ias %lu-bit, oas %lu-bit (features 0x%08x)\n",
|
||||
smmu->ias, smmu->oas, smmu->features);
|
||||
dev_info(smmu->dev, "oas %lu-bit (features 0x%08x)\n",
|
||||
smmu->oas, smmu->features);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ struct arm_vsmmu;
|
||||
#define IDR0_COHACC (1 << 4)
|
||||
#define IDR0_TTF GENMASK(3, 2)
|
||||
#define IDR0_TTF_AARCH64 2
|
||||
#define IDR0_TTF_AARCH32_64 3
|
||||
#define IDR0_S1P (1 << 1)
|
||||
#define IDR0_S2P (1 << 0)
|
||||
|
||||
@@ -782,7 +781,6 @@ struct arm_smmu_device {
|
||||
int gerr_irq;
|
||||
int combined_irq;
|
||||
|
||||
unsigned long ias; /* IPA */
|
||||
unsigned long oas; /* PA */
|
||||
unsigned long pgsize_bitmap;
|
||||
|
||||
@@ -898,6 +896,8 @@ struct arm_smmu_entry_writer {
|
||||
|
||||
struct arm_smmu_entry_writer_ops {
|
||||
void (*get_used)(const __le64 *entry, __le64 *used);
|
||||
void (*get_update_safe)(const __le64 *cur, const __le64 *target,
|
||||
__le64 *safe_bits);
|
||||
void (*sync)(struct arm_smmu_entry_writer *writer);
|
||||
};
|
||||
|
||||
@@ -909,6 +909,8 @@ void arm_smmu_make_s2_domain_ste(struct arm_smmu_ste *target,
|
||||
|
||||
#if IS_ENABLED(CONFIG_KUNIT)
|
||||
void arm_smmu_get_ste_used(const __le64 *ent, __le64 *used_bits);
|
||||
void arm_smmu_get_ste_update_safe(const __le64 *cur, const __le64 *target,
|
||||
__le64 *safe_bits);
|
||||
void arm_smmu_write_entry(struct arm_smmu_entry_writer *writer, __le64 *cur,
|
||||
const __le64 *target);
|
||||
void arm_smmu_get_cd_used(const __le64 *ent, __le64 *used_bits);
|
||||
|
||||
@@ -431,17 +431,19 @@ static int qcom_smmu_cfg_probe(struct arm_smmu_device *smmu)
|
||||
|
||||
/*
|
||||
* Some platforms support more than the Arm SMMU architected maximum of
|
||||
* 128 stream matching groups. For unknown reasons, the additional
|
||||
* groups don't exhibit the same behavior as the architected registers,
|
||||
* so limit the groups to 128 until the behavior is fixed for the other
|
||||
* groups.
|
||||
* 128 stream matching groups. The additional registers appear to have
|
||||
* the same behavior as the architected registers in the hardware.
|
||||
* However, on some firmware versions, the hypervisor does not
|
||||
* correctly trap and emulate accesses to the additional registers,
|
||||
* resulting in unexpected behavior.
|
||||
*
|
||||
* If there are more than 128 groups, use the last reliable group to
|
||||
* detect if we need to apply the bypass quirk.
|
||||
*/
|
||||
if (smmu->num_mapping_groups > 128) {
|
||||
dev_notice(smmu->dev, "\tLimiting the stream matching groups to 128\n");
|
||||
smmu->num_mapping_groups = 128;
|
||||
}
|
||||
|
||||
last_s2cr = ARM_SMMU_GR0_S2CR(smmu->num_mapping_groups - 1);
|
||||
if (smmu->num_mapping_groups > 128)
|
||||
last_s2cr = ARM_SMMU_GR0_S2CR(127);
|
||||
else
|
||||
last_s2cr = ARM_SMMU_GR0_S2CR(smmu->num_mapping_groups - 1);
|
||||
|
||||
/*
|
||||
* With some firmware versions writes to S2CR of type FAULT are
|
||||
@@ -464,6 +466,11 @@ static int qcom_smmu_cfg_probe(struct arm_smmu_device *smmu)
|
||||
|
||||
reg = FIELD_PREP(ARM_SMMU_CBAR_TYPE, CBAR_TYPE_S1_TRANS_S2_BYPASS);
|
||||
arm_smmu_gr1_write(smmu, ARM_SMMU_GR1_CBAR(qsmmu->bypass_cbndx), reg);
|
||||
|
||||
if (smmu->num_mapping_groups > 128) {
|
||||
dev_notice(smmu->dev, "\tLimiting the stream matching groups to 128\n");
|
||||
smmu->num_mapping_groups = 128;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < smmu->num_mapping_groups; i++) {
|
||||
|
||||
@@ -66,7 +66,7 @@ config INTEL_IOMMU_DEFAULT_ON
|
||||
|
||||
config INTEL_IOMMU_FLOPPY_WA
|
||||
def_bool y
|
||||
depends on X86
|
||||
depends on X86 && BLK_DEV_FD
|
||||
help
|
||||
Floppy disk drivers are known to bypass DMA API calls
|
||||
thereby failing to work when IOMMU is enabled. This
|
||||
|
||||
@@ -363,6 +363,13 @@ static void qi_batch_add_pasid_dev_iotlb(struct intel_iommu *iommu, u16 sid, u16
|
||||
qi_batch_increment_index(iommu, batch);
|
||||
}
|
||||
|
||||
static bool intel_domain_use_piotlb(struct dmar_domain *domain)
|
||||
{
|
||||
return domain->domain.type == IOMMU_DOMAIN_SVA ||
|
||||
domain->domain.type == IOMMU_DOMAIN_NESTED ||
|
||||
intel_domain_is_fs_paging(domain);
|
||||
}
|
||||
|
||||
static void cache_tag_flush_iotlb(struct dmar_domain *domain, struct cache_tag *tag,
|
||||
unsigned long addr, unsigned long pages,
|
||||
unsigned long mask, int ih)
|
||||
@@ -370,7 +377,7 @@ static void cache_tag_flush_iotlb(struct dmar_domain *domain, struct cache_tag *
|
||||
struct intel_iommu *iommu = tag->iommu;
|
||||
u64 type = DMA_TLB_PSI_FLUSH;
|
||||
|
||||
if (intel_domain_is_fs_paging(domain)) {
|
||||
if (intel_domain_use_piotlb(domain)) {
|
||||
qi_batch_add_piotlb(iommu, tag->domain_id, tag->pasid, addr,
|
||||
pages, ih, domain->qi_batch);
|
||||
return;
|
||||
|
||||
@@ -1314,7 +1314,6 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
|
||||
if (fault & DMA_FSTS_ITE) {
|
||||
head = readl(iommu->reg + DMAR_IQH_REG);
|
||||
head = ((head >> shift) - 1 + QI_LENGTH) % QI_LENGTH;
|
||||
head |= 1;
|
||||
tail = readl(iommu->reg + DMAR_IQT_REG);
|
||||
tail = ((tail >> shift) - 1 + QI_LENGTH) % QI_LENGTH;
|
||||
|
||||
@@ -1331,7 +1330,7 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
|
||||
do {
|
||||
if (qi->desc_status[head] == QI_IN_USE)
|
||||
qi->desc_status[head] = QI_ABORT;
|
||||
head = (head - 2 + QI_LENGTH) % QI_LENGTH;
|
||||
head = (head - 1 + QI_LENGTH) % QI_LENGTH;
|
||||
} while (head != tail);
|
||||
|
||||
/*
|
||||
|
||||
+16
-19
@@ -1722,22 +1722,22 @@ static void domain_context_clear_one(struct device_domain_info *info, u8 bus, u8
|
||||
}
|
||||
|
||||
did = context_domain_id(context);
|
||||
context_clear_entry(context);
|
||||
context_clear_present(context);
|
||||
__iommu_flush_cache(iommu, context, sizeof(*context));
|
||||
spin_unlock(&iommu->lock);
|
||||
intel_context_flush_no_pasid(info, context, did);
|
||||
context_clear_entry(context);
|
||||
__iommu_flush_cache(iommu, context, sizeof(*context));
|
||||
}
|
||||
|
||||
int __domain_setup_first_level(struct intel_iommu *iommu, struct device *dev,
|
||||
ioasid_t pasid, u16 did, phys_addr_t fsptptr,
|
||||
int flags, struct iommu_domain *old)
|
||||
{
|
||||
if (!old)
|
||||
return intel_pasid_setup_first_level(iommu, dev, fsptptr, pasid,
|
||||
did, flags);
|
||||
return intel_pasid_replace_first_level(iommu, dev, fsptptr, pasid, did,
|
||||
iommu_domain_did(old, iommu),
|
||||
flags);
|
||||
if (old)
|
||||
intel_pasid_tear_down_entry(iommu, dev, pasid, false);
|
||||
|
||||
return intel_pasid_setup_first_level(iommu, dev, fsptptr, pasid, did, flags);
|
||||
}
|
||||
|
||||
static int domain_setup_second_level(struct intel_iommu *iommu,
|
||||
@@ -1745,23 +1745,20 @@ static int domain_setup_second_level(struct intel_iommu *iommu,
|
||||
struct device *dev, ioasid_t pasid,
|
||||
struct iommu_domain *old)
|
||||
{
|
||||
if (!old)
|
||||
return intel_pasid_setup_second_level(iommu, domain,
|
||||
dev, pasid);
|
||||
return intel_pasid_replace_second_level(iommu, domain, dev,
|
||||
iommu_domain_did(old, iommu),
|
||||
pasid);
|
||||
if (old)
|
||||
intel_pasid_tear_down_entry(iommu, dev, pasid, false);
|
||||
|
||||
return intel_pasid_setup_second_level(iommu, domain, dev, pasid);
|
||||
}
|
||||
|
||||
static int domain_setup_passthrough(struct intel_iommu *iommu,
|
||||
struct device *dev, ioasid_t pasid,
|
||||
struct iommu_domain *old)
|
||||
{
|
||||
if (!old)
|
||||
return intel_pasid_setup_pass_through(iommu, dev, pasid);
|
||||
return intel_pasid_replace_pass_through(iommu, dev,
|
||||
iommu_domain_did(old, iommu),
|
||||
pasid);
|
||||
if (old)
|
||||
intel_pasid_tear_down_entry(iommu, dev, pasid, false);
|
||||
|
||||
return intel_pasid_setup_pass_through(iommu, dev, pasid);
|
||||
}
|
||||
|
||||
static int domain_setup_first_level(struct intel_iommu *iommu,
|
||||
@@ -3817,7 +3814,7 @@ static struct iommu_device *intel_iommu_probe_device(struct device *dev)
|
||||
}
|
||||
|
||||
if (info->ats_supported && ecap_prs(iommu->ecap) &&
|
||||
pci_pri_supported(pdev))
|
||||
ecap_pds(iommu->ecap) && pci_pri_supported(pdev))
|
||||
info->pri_supported = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -973,7 +973,26 @@ static inline unsigned long lvl_to_nr_pages(unsigned int lvl)
|
||||
|
||||
static inline void context_set_present(struct context_entry *context)
|
||||
{
|
||||
context->lo |= 1;
|
||||
u64 val;
|
||||
|
||||
dma_wmb();
|
||||
val = READ_ONCE(context->lo) | 1;
|
||||
WRITE_ONCE(context->lo, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the Present (P) bit (bit 0) of a context table entry. This initiates
|
||||
* the transition of the entry's ownership from hardware to software. The
|
||||
* caller is responsible for fulfilling the invalidation handshake recommended
|
||||
* by the VT-d spec, Section 6.5.3.3 (Guidance to Software for Invalidations).
|
||||
*/
|
||||
static inline void context_clear_present(struct context_entry *context)
|
||||
{
|
||||
u64 val;
|
||||
|
||||
val = READ_ONCE(context->lo) & GENMASK_ULL(63, 1);
|
||||
WRITE_ONCE(context->lo, val);
|
||||
dma_wmb();
|
||||
}
|
||||
|
||||
static inline void context_set_fault_enable(struct context_entry *context)
|
||||
@@ -1101,7 +1120,7 @@ static inline void qi_desc_iotlb(struct intel_iommu *iommu, u16 did, u64 addr,
|
||||
struct qi_desc *desc)
|
||||
{
|
||||
u8 dw = 0, dr = 0;
|
||||
int ih = 0;
|
||||
int ih = addr & 1;
|
||||
|
||||
if (cap_write_drain(iommu->cap))
|
||||
dw = 1;
|
||||
|
||||
@@ -141,11 +141,10 @@ static int domain_setup_nested(struct intel_iommu *iommu,
|
||||
struct device *dev, ioasid_t pasid,
|
||||
struct iommu_domain *old)
|
||||
{
|
||||
if (!old)
|
||||
return intel_pasid_setup_nested(iommu, dev, pasid, domain);
|
||||
return intel_pasid_replace_nested(iommu, dev, pasid,
|
||||
iommu_domain_did(old, iommu),
|
||||
domain);
|
||||
if (old)
|
||||
intel_pasid_tear_down_entry(iommu, dev, pasid, false);
|
||||
|
||||
return intel_pasid_setup_nested(iommu, dev, pasid, domain);
|
||||
}
|
||||
|
||||
static int intel_nested_set_dev_pasid(struct iommu_domain *domain,
|
||||
|
||||
+22
-196
@@ -153,6 +153,9 @@ retry:
|
||||
if (!entries)
|
||||
return NULL;
|
||||
|
||||
if (!ecap_coherent(info->iommu->ecap))
|
||||
clflush_cache_range(entries, VTD_PAGE_SIZE);
|
||||
|
||||
/*
|
||||
* The pasid directory table entry won't be freed after
|
||||
* allocation. No worry about the race with free and
|
||||
@@ -165,10 +168,8 @@ retry:
|
||||
iommu_free_pages(entries);
|
||||
goto retry;
|
||||
}
|
||||
if (!ecap_coherent(info->iommu->ecap)) {
|
||||
clflush_cache_range(entries, VTD_PAGE_SIZE);
|
||||
if (!ecap_coherent(info->iommu->ecap))
|
||||
clflush_cache_range(&dir[dir_index].val, sizeof(*dir));
|
||||
}
|
||||
}
|
||||
|
||||
return &entries[index];
|
||||
@@ -218,7 +219,7 @@ devtlb_invalidation_with_pasid(struct intel_iommu *iommu,
|
||||
if (!info || !info->ats_enabled)
|
||||
return;
|
||||
|
||||
if (pci_dev_is_disconnected(to_pci_dev(dev)))
|
||||
if (!pci_device_is_present(to_pci_dev(dev)))
|
||||
return;
|
||||
|
||||
sid = PCI_DEVID(info->bus, info->devfn);
|
||||
@@ -272,7 +273,7 @@ void intel_pasid_tear_down_entry(struct intel_iommu *iommu, struct device *dev,
|
||||
|
||||
did = pasid_get_domain_id(pte);
|
||||
pgtt = pasid_pte_get_pgtt(pte);
|
||||
intel_pasid_clear_entry(dev, pasid, fault_ignore);
|
||||
pasid_clear_present(pte);
|
||||
spin_unlock(&iommu->lock);
|
||||
|
||||
if (!ecap_coherent(iommu->ecap))
|
||||
@@ -286,6 +287,10 @@ void intel_pasid_tear_down_entry(struct intel_iommu *iommu, struct device *dev,
|
||||
iommu->flush.flush_iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH);
|
||||
|
||||
devtlb_invalidation_with_pasid(iommu, dev, pasid);
|
||||
intel_pasid_clear_entry(dev, pasid, fault_ignore);
|
||||
if (!ecap_coherent(iommu->ecap))
|
||||
clflush_cache_range(pte, sizeof(*pte));
|
||||
|
||||
if (!fault_ignore)
|
||||
intel_iommu_drain_pasid_prq(dev, pasid);
|
||||
}
|
||||
@@ -412,50 +417,6 @@ int intel_pasid_setup_first_level(struct intel_iommu *iommu, struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int intel_pasid_replace_first_level(struct intel_iommu *iommu,
|
||||
struct device *dev, phys_addr_t fsptptr,
|
||||
u32 pasid, u16 did, u16 old_did,
|
||||
int flags)
|
||||
{
|
||||
struct pasid_entry *pte, new_pte;
|
||||
|
||||
if (!ecap_flts(iommu->ecap)) {
|
||||
pr_err("No first level translation support on %s\n",
|
||||
iommu->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((flags & PASID_FLAG_FL5LP) && !cap_fl5lp_support(iommu->cap)) {
|
||||
pr_err("No 5-level paging support for first-level on %s\n",
|
||||
iommu->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pasid_pte_config_first_level(iommu, &new_pte, fsptptr, did, flags);
|
||||
|
||||
spin_lock(&iommu->lock);
|
||||
pte = intel_pasid_get_entry(dev, pasid);
|
||||
if (!pte) {
|
||||
spin_unlock(&iommu->lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!pasid_pte_is_present(pte)) {
|
||||
spin_unlock(&iommu->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
WARN_ON(old_did != pasid_get_domain_id(pte));
|
||||
|
||||
*pte = new_pte;
|
||||
spin_unlock(&iommu->lock);
|
||||
|
||||
intel_pasid_flush_present(iommu, dev, pasid, old_did, pte);
|
||||
intel_iommu_drain_pasid_prq(dev, pasid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the scalable mode pasid entry for second only translation type.
|
||||
*/
|
||||
@@ -523,57 +484,6 @@ int intel_pasid_setup_second_level(struct intel_iommu *iommu,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int intel_pasid_replace_second_level(struct intel_iommu *iommu,
|
||||
struct dmar_domain *domain,
|
||||
struct device *dev, u16 old_did,
|
||||
u32 pasid)
|
||||
{
|
||||
struct pasid_entry *pte, new_pte;
|
||||
struct dma_pte *pgd;
|
||||
u64 pgd_val;
|
||||
u16 did;
|
||||
|
||||
/*
|
||||
* If hardware advertises no support for second level
|
||||
* translation, return directly.
|
||||
*/
|
||||
if (!ecap_slts(iommu->ecap)) {
|
||||
pr_err("No second level translation support on %s\n",
|
||||
iommu->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pgd = domain->pgd;
|
||||
pgd_val = virt_to_phys(pgd);
|
||||
did = domain_id_iommu(domain, iommu);
|
||||
|
||||
pasid_pte_config_second_level(iommu, &new_pte, pgd_val,
|
||||
domain->agaw, did,
|
||||
domain->dirty_tracking);
|
||||
|
||||
spin_lock(&iommu->lock);
|
||||
pte = intel_pasid_get_entry(dev, pasid);
|
||||
if (!pte) {
|
||||
spin_unlock(&iommu->lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!pasid_pte_is_present(pte)) {
|
||||
spin_unlock(&iommu->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
WARN_ON(old_did != pasid_get_domain_id(pte));
|
||||
|
||||
*pte = new_pte;
|
||||
spin_unlock(&iommu->lock);
|
||||
|
||||
intel_pasid_flush_present(iommu, dev, pasid, old_did, pte);
|
||||
intel_iommu_drain_pasid_prq(dev, pasid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up dirty tracking on a second only or nested translation type.
|
||||
*/
|
||||
@@ -686,38 +596,6 @@ int intel_pasid_setup_pass_through(struct intel_iommu *iommu,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int intel_pasid_replace_pass_through(struct intel_iommu *iommu,
|
||||
struct device *dev, u16 old_did,
|
||||
u32 pasid)
|
||||
{
|
||||
struct pasid_entry *pte, new_pte;
|
||||
u16 did = FLPT_DEFAULT_DID;
|
||||
|
||||
pasid_pte_config_pass_through(iommu, &new_pte, did);
|
||||
|
||||
spin_lock(&iommu->lock);
|
||||
pte = intel_pasid_get_entry(dev, pasid);
|
||||
if (!pte) {
|
||||
spin_unlock(&iommu->lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!pasid_pte_is_present(pte)) {
|
||||
spin_unlock(&iommu->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
WARN_ON(old_did != pasid_get_domain_id(pte));
|
||||
|
||||
*pte = new_pte;
|
||||
spin_unlock(&iommu->lock);
|
||||
|
||||
intel_pasid_flush_present(iommu, dev, pasid, old_did, pte);
|
||||
intel_iommu_drain_pasid_prq(dev, pasid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the page snoop control for a pasid entry which has been set up.
|
||||
*/
|
||||
@@ -848,69 +726,6 @@ int intel_pasid_setup_nested(struct intel_iommu *iommu, struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int intel_pasid_replace_nested(struct intel_iommu *iommu,
|
||||
struct device *dev, u32 pasid,
|
||||
u16 old_did, struct dmar_domain *domain)
|
||||
{
|
||||
struct iommu_hwpt_vtd_s1 *s1_cfg = &domain->s1_cfg;
|
||||
struct dmar_domain *s2_domain = domain->s2_domain;
|
||||
u16 did = domain_id_iommu(domain, iommu);
|
||||
struct pasid_entry *pte, new_pte;
|
||||
|
||||
/* Address width should match the address width supported by hardware */
|
||||
switch (s1_cfg->addr_width) {
|
||||
case ADDR_WIDTH_4LEVEL:
|
||||
break;
|
||||
case ADDR_WIDTH_5LEVEL:
|
||||
if (!cap_fl5lp_support(iommu->cap)) {
|
||||
dev_err_ratelimited(dev,
|
||||
"5-level paging not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err_ratelimited(dev, "Invalid stage-1 address width %d\n",
|
||||
s1_cfg->addr_width);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((s1_cfg->flags & IOMMU_VTD_S1_SRE) && !ecap_srs(iommu->ecap)) {
|
||||
pr_err_ratelimited("No supervisor request support on %s\n",
|
||||
iommu->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((s1_cfg->flags & IOMMU_VTD_S1_EAFE) && !ecap_eafs(iommu->ecap)) {
|
||||
pr_err_ratelimited("No extended access flag support on %s\n",
|
||||
iommu->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pasid_pte_config_nestd(iommu, &new_pte, s1_cfg, s2_domain, did);
|
||||
|
||||
spin_lock(&iommu->lock);
|
||||
pte = intel_pasid_get_entry(dev, pasid);
|
||||
if (!pte) {
|
||||
spin_unlock(&iommu->lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!pasid_pte_is_present(pte)) {
|
||||
spin_unlock(&iommu->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
WARN_ON(old_did != pasid_get_domain_id(pte));
|
||||
|
||||
*pte = new_pte;
|
||||
spin_unlock(&iommu->lock);
|
||||
|
||||
intel_pasid_flush_present(iommu, dev, pasid, old_did, pte);
|
||||
intel_iommu_drain_pasid_prq(dev, pasid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interfaces to setup or teardown a pasid table to the scalable-mode
|
||||
* context table entry:
|
||||
@@ -1023,7 +838,7 @@ static int device_pasid_table_setup(struct device *dev, u8 bus, u8 devfn)
|
||||
}
|
||||
|
||||
if (context_copied(iommu, bus, devfn)) {
|
||||
context_clear_entry(context);
|
||||
context_clear_present(context);
|
||||
__iommu_flush_cache(iommu, context, sizeof(*context));
|
||||
|
||||
/*
|
||||
@@ -1043,6 +858,9 @@ static int device_pasid_table_setup(struct device *dev, u8 bus, u8 devfn)
|
||||
iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
|
||||
devtlb_invalidation_with_pasid(iommu, dev, IOMMU_NO_PASID);
|
||||
|
||||
context_clear_entry(context);
|
||||
__iommu_flush_cache(iommu, context, sizeof(*context));
|
||||
|
||||
/*
|
||||
* At this point, the device is supposed to finish reset at
|
||||
* its driver probe stage, so no in-flight DMA will exist,
|
||||
@@ -1106,6 +924,14 @@ static void __context_flush_dev_iotlb(struct device_domain_info *info)
|
||||
if (!info->ats_enabled)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Skip dev-IOTLB flush for inaccessible PCIe devices to prevent the
|
||||
* Intel IOMMU from waiting indefinitely for an ATS invalidation that
|
||||
* cannot complete.
|
||||
*/
|
||||
if (!pci_device_is_present(to_pci_dev(info->dev)))
|
||||
return;
|
||||
|
||||
qi_flush_dev_iotlb(info->iommu, PCI_DEVID(info->bus, info->devfn),
|
||||
info->pfsid, info->ats_qdep, 0, MAX_AGAW_PFN_WIDTH);
|
||||
|
||||
|
||||
+14
-14
@@ -233,9 +233,23 @@ static inline void pasid_set_wpe(struct pasid_entry *pe)
|
||||
*/
|
||||
static inline void pasid_set_present(struct pasid_entry *pe)
|
||||
{
|
||||
dma_wmb();
|
||||
pasid_set_bits(&pe->val[0], 1 << 0, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the Present (P) bit (bit 0) of a scalable-mode PASID table entry.
|
||||
* This initiates the transition of the entry's ownership from hardware
|
||||
* to software. The caller is responsible for fulfilling the invalidation
|
||||
* handshake recommended by the VT-d spec, Section 6.5.3.3 (Guidance to
|
||||
* Software for Invalidations).
|
||||
*/
|
||||
static inline void pasid_clear_present(struct pasid_entry *pe)
|
||||
{
|
||||
pasid_set_bits(&pe->val[0], 1 << 0, 0);
|
||||
dma_wmb();
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup Page Walk Snoop bit (Bit 87) of a scalable mode PASID
|
||||
* entry.
|
||||
@@ -301,20 +315,6 @@ int intel_pasid_setup_pass_through(struct intel_iommu *iommu,
|
||||
struct device *dev, u32 pasid);
|
||||
int intel_pasid_setup_nested(struct intel_iommu *iommu, struct device *dev,
|
||||
u32 pasid, struct dmar_domain *domain);
|
||||
int intel_pasid_replace_first_level(struct intel_iommu *iommu,
|
||||
struct device *dev, phys_addr_t fsptptr,
|
||||
u32 pasid, u16 did, u16 old_did, int flags);
|
||||
int intel_pasid_replace_second_level(struct intel_iommu *iommu,
|
||||
struct dmar_domain *domain,
|
||||
struct device *dev, u16 old_did,
|
||||
u32 pasid);
|
||||
int intel_pasid_replace_pass_through(struct intel_iommu *iommu,
|
||||
struct device *dev, u16 old_did,
|
||||
u32 pasid);
|
||||
int intel_pasid_replace_nested(struct intel_iommu *iommu,
|
||||
struct device *dev, u32 pasid,
|
||||
u16 old_did, struct dmar_domain *domain);
|
||||
|
||||
void intel_pasid_tear_down_entry(struct intel_iommu *iommu,
|
||||
struct device *dev, u32 pasid,
|
||||
bool fault_ignore);
|
||||
|
||||
@@ -164,9 +164,12 @@ static int intel_svm_set_dev_pasid(struct iommu_domain *domain,
|
||||
if (IS_ERR(dev_pasid))
|
||||
return PTR_ERR(dev_pasid);
|
||||
|
||||
ret = iopf_for_domain_replace(domain, old, dev);
|
||||
if (ret)
|
||||
goto out_remove_dev_pasid;
|
||||
/* SVA with non-IOMMU/PRI IOPF handling is allowed. */
|
||||
if (info->pri_supported) {
|
||||
ret = iopf_for_domain_replace(domain, old, dev);
|
||||
if (ret)
|
||||
goto out_remove_dev_pasid;
|
||||
}
|
||||
|
||||
/* Setup the pasid table: */
|
||||
sflags = cpu_feature_enabled(X86_FEATURE_LA57) ? PASID_FLAG_FL5LP : 0;
|
||||
@@ -180,7 +183,8 @@ static int intel_svm_set_dev_pasid(struct iommu_domain *domain,
|
||||
|
||||
return 0;
|
||||
out_unwind_iopf:
|
||||
iopf_for_domain_replace(old, domain, dev);
|
||||
if (info->pri_supported)
|
||||
iopf_for_domain_replace(old, domain, dev);
|
||||
out_remove_dev_pasid:
|
||||
domain_remove_dev_pasid(domain, dev, pasid);
|
||||
return ret;
|
||||
|
||||
@@ -638,7 +638,7 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
|
||||
pte = READ_ONCE(*ptep);
|
||||
if (!pte) {
|
||||
WARN_ON(!(data->iop.cfg.quirks & IO_PGTABLE_QUIRK_NO_WARN));
|
||||
return -ENOENT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If the size matches this level, we're in the right place */
|
||||
|
||||
@@ -707,7 +707,8 @@ static int iopt_unmap_iova_range(struct io_pagetable *iopt, unsigned long start,
|
||||
struct iopt_area *area;
|
||||
unsigned long unmapped_bytes = 0;
|
||||
unsigned int tries = 0;
|
||||
int rc = -ENOENT;
|
||||
/* If there are no mapped entries then success */
|
||||
int rc = 0;
|
||||
|
||||
/*
|
||||
* The domains_rwsem must be held in read mode any time any area->pages
|
||||
@@ -777,8 +778,6 @@ again:
|
||||
|
||||
down_write(&iopt->iova_rwsem);
|
||||
}
|
||||
if (unmapped_bytes)
|
||||
rc = 0;
|
||||
|
||||
out_unlock_iova:
|
||||
up_write(&iopt->iova_rwsem);
|
||||
@@ -815,13 +814,8 @@ int iopt_unmap_iova(struct io_pagetable *iopt, unsigned long iova,
|
||||
|
||||
int iopt_unmap_all(struct io_pagetable *iopt, unsigned long *unmapped)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = iopt_unmap_iova_range(iopt, 0, ULONG_MAX, unmapped);
|
||||
/* If the IOVAs are empty then unmap all succeeds */
|
||||
if (rc == -ENOENT)
|
||||
return 0;
|
||||
return rc;
|
||||
return iopt_unmap_iova_range(iopt, 0, ULONG_MAX, unmapped);
|
||||
}
|
||||
|
||||
/* The caller must always free all the nodes in the allowed_iova rb_root. */
|
||||
|
||||
@@ -367,6 +367,10 @@ int iommufd_ioas_unmap(struct iommufd_ucmd *ucmd)
|
||||
&unmapped);
|
||||
if (rc)
|
||||
goto out_put;
|
||||
if (!unmapped) {
|
||||
rc = -ENOENT;
|
||||
goto out_put;
|
||||
}
|
||||
}
|
||||
|
||||
cmd->length = unmapped;
|
||||
|
||||
@@ -720,6 +720,8 @@ static int ipmmu_init_platform_device(struct device *dev,
|
||||
|
||||
dev_iommu_priv_set(dev, platform_get_drvdata(ipmmu_pdev));
|
||||
|
||||
put_device(&ipmmu_pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -830,10 +830,9 @@ static struct tegra_smmu *tegra_smmu_find(struct device_node *np)
|
||||
return NULL;
|
||||
|
||||
mc = platform_get_drvdata(pdev);
|
||||
if (!mc) {
|
||||
put_device(&pdev->dev);
|
||||
put_device(&pdev->dev);
|
||||
if (!mc)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return mc->smmu;
|
||||
}
|
||||
|
||||
@@ -27,5 +27,6 @@ struct map_benchmark {
|
||||
__u32 dma_dir; /* DMA data direction */
|
||||
__u32 dma_trans_ns; /* time for DMA transmission in ns */
|
||||
__u32 granule; /* how many PAGE_SIZE will do map/unmap once a time */
|
||||
__u8 expansion[76]; /* For future use */
|
||||
};
|
||||
#endif /* _KERNEL_DMA_BENCHMARK_H */
|
||||
|
||||
@@ -75,8 +75,9 @@ static inline const struct of_device_id *of_match_device(
|
||||
}
|
||||
|
||||
static inline int of_dma_configure_id(struct device *dev,
|
||||
struct device_node *np,
|
||||
bool force_dma)
|
||||
struct device_node *np,
|
||||
bool force_dma,
|
||||
const u32 *id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -486,6 +486,7 @@ int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents,
|
||||
case PCI_P2PDMA_MAP_BUS_ADDR:
|
||||
sg->dma_address = pci_p2pdma_bus_addr_map(&p2pdma_state,
|
||||
sg_phys(sg));
|
||||
sg_dma_len(sg) = sg->length;
|
||||
sg_dma_mark_bus_address(sg);
|
||||
continue;
|
||||
default:
|
||||
|
||||
+7
-2
@@ -93,7 +93,7 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
|
||||
page = dma_alloc_from_contiguous(NULL, 1 << order,
|
||||
order, false);
|
||||
if (!page)
|
||||
page = alloc_pages(gfp, order);
|
||||
page = alloc_pages(gfp | __GFP_NOWARN, order);
|
||||
} while (!page && order-- > 0);
|
||||
if (!page)
|
||||
goto out;
|
||||
@@ -268,15 +268,20 @@ struct page *dma_alloc_from_pool(struct device *dev, size_t size,
|
||||
{
|
||||
struct gen_pool *pool = NULL;
|
||||
struct page *page;
|
||||
bool pool_found = false;
|
||||
|
||||
while ((pool = dma_guess_pool(pool, gfp))) {
|
||||
pool_found = true;
|
||||
page = __dma_alloc_from_pool(dev, size, pool, cpu_addr,
|
||||
phys_addr_ok);
|
||||
if (page)
|
||||
return page;
|
||||
}
|
||||
|
||||
WARN(1, "Failed to get suitable pool for %s\n", dev_name(dev));
|
||||
if (pool_found)
|
||||
WARN(!(gfp & __GFP_NOWARN), "DMA pool exhausted for %s\n", dev_name(dev));
|
||||
else
|
||||
WARN(1, "Failed to get suitable pool for %s\n", dev_name(dev));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,8 +88,6 @@ static void sg_split_phys(struct sg_splitter *splitters, const int nb_splits)
|
||||
if (!j) {
|
||||
out_sg->offset += split->skip_sg0;
|
||||
out_sg->length -= split->skip_sg0;
|
||||
} else {
|
||||
out_sg->offset = 0;
|
||||
}
|
||||
sg_dma_address(out_sg) = 0;
|
||||
sg_dma_len(out_sg) = 0;
|
||||
|
||||
@@ -2638,6 +2638,8 @@ TEST_F(vfio_compat_mock_domain, map)
|
||||
ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_MAP_DMA, &map_cmd));
|
||||
ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA, &unmap_cmd));
|
||||
ASSERT_EQ(BUFFER_SIZE, unmap_cmd.size);
|
||||
/* Unmap of empty is success */
|
||||
ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_UNMAP_DMA, &unmap_cmd));
|
||||
|
||||
/* UNMAP_FLAG_ALL requires 0 iova/size */
|
||||
ASSERT_EQ(0, ioctl(self->fd, VFIO_IOMMU_MAP_DMA, &map_cmd));
|
||||
|
||||
Reference in New Issue
Block a user