Skip to content

Commit

Permalink
Merge tag 'iommu-fixes' of git://git.kernel.org/pub/scm/linux/kernel/…
Browse files Browse the repository at this point in the history
…git/arm64/linux

Pull iommu fixes from Will Deacon:
 "This is mainly all Intel VT-D stuff, but there are some fixes for AMD
  and ARM as well.

  We've also got the revert I promised during the merge window, which
  removes a temporary hack to accomodate i915 while we transitioned the
  Intel IOMMU driver over to the common DMA-IOMMU API.

  Finally, there are still a couple of other VT-D fixes floating around,
  so I expect to send you another batch of fixes next week.

  Summary:

   - Fix VT-D TLB invalidation for subdevices

   - Fix VT-D use-after-free on subdevice detach

   - Fix VT-D locking so that IRQs are disabled during SVA bind/unbind

   - Fix VT-D address alignment when flushing IOTLB

   - Fix memory leak in VT-D IRQ remapping failure path

   - Revert temporary i915 sglist hack now that it is no longer required

   - Fix sporadic boot failure with Arm SMMU on Qualcomm SM8150

   - Fix NULL dereference in AMD IRQ remapping code with remapping disabled

   - Fix accidental enabling of irqs on AMD resume-from-suspend path

   - Fix some typos in comments"

* tag 'iommu-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux:
  iommu/vt-d: Fix ineffective devTLB invalidation for subdevices
  iommu/vt-d: Fix general protection fault in aux_detach_device()
  iommu/vt-d: Move intel_iommu info from struct intel_svm to struct intel_svm_dev
  iommu/arm-smmu-qcom: Initialize SCTLR of the bypass context
  iommu/vt-d: Fix lockdep splat in sva bind()/unbind()
  Revert "iommu: Add quirk for Intel graphic devices in map_sg"
  iommu/vt-d: Fix misuse of ALIGN in qi_flush_piotlb()
  iommu/amd: Stop irq_remapping_select() matching when remapping is disabled
  iommu/amd: Set iommu->int_enabled consistently when interrupts are set up
  iommu/intel: Fix memleak in intel_irq_remapping_alloc
  iommu/iova: fix 'domain' typos
  • Loading branch information
torvalds committed Jan 8, 2021
2 parents 95f0505 + 7c29ada commit 3e2a590
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 91 deletions.
3 changes: 1 addition & 2 deletions drivers/iommu/amd/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -1973,8 +1973,6 @@ static int iommu_setup_msi(struct amd_iommu *iommu)
return r;
}

iommu->int_enabled = true;

return 0;
}

Expand Down Expand Up @@ -2169,6 +2167,7 @@ static int iommu_init_irq(struct amd_iommu *iommu)
if (ret)
return ret;

iommu->int_enabled = true;
enable_faults:
iommu_feature_enable(iommu, CONTROL_EVT_INT_EN);

Expand Down
3 changes: 3 additions & 0 deletions drivers/iommu/amd/iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -3854,6 +3854,9 @@ static int irq_remapping_select(struct irq_domain *d, struct irq_fwspec *fwspec,
struct amd_iommu *iommu;
int devid = -1;

if (!amd_iommu_irq_remap)
return 0;

if (x86_fwspec_is_ioapic(fwspec))
devid = get_ioapic_devid(fwspec->param[0]);
else if (x86_fwspec_is_hpet(fwspec))
Expand Down
2 changes: 2 additions & 0 deletions drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ static int qcom_smmu_cfg_probe(struct arm_smmu_device *smmu)

set_bit(qsmmu->bypass_cbndx, smmu->context_map);

arm_smmu_cb_write(smmu, qsmmu->bypass_cbndx, ARM_SMMU_CB_SCTLR, 0);

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);
}
Expand Down
27 changes: 0 additions & 27 deletions drivers/iommu/dma-iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -863,33 +863,6 @@ static int __finalise_sg(struct device *dev, struct scatterlist *sg, int nents,
unsigned int cur_len = 0, max_len = dma_get_max_seg_size(dev);
int i, count = 0;

/*
* The Intel graphic driver is used to assume that the returned
* sg list is not combound. This blocks the efforts of converting
* Intel IOMMU driver to dma-iommu api's. Add this quirk to make the
* device driver work and should be removed once it's fixed in i915
* driver.
*/
if (IS_ENABLED(CONFIG_DRM_I915) && dev_is_pci(dev) &&
to_pci_dev(dev)->vendor == PCI_VENDOR_ID_INTEL &&
(to_pci_dev(dev)->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
for_each_sg(sg, s, nents, i) {
unsigned int s_iova_off = sg_dma_address(s);
unsigned int s_length = sg_dma_len(s);
unsigned int s_iova_len = s->length;

s->offset += s_iova_off;
s->length = s_length;
sg_dma_address(s) = dma_addr + s_iova_off;
sg_dma_len(s) = s_length;
dma_addr += s_iova_len;

pr_info_once("sg combining disabled due to i915 driver\n");
}

return nents;
}

for_each_sg(sg, s, nents, i) {
/* Restore this segment's original unaligned fields first */
unsigned int s_iova_off = sg_dma_address(s);
Expand Down
4 changes: 2 additions & 2 deletions drivers/iommu/intel/dmar.c
Original file line number Diff line number Diff line change
Expand Up @@ -1461,8 +1461,8 @@ void qi_flush_piotlb(struct intel_iommu *iommu, u16 did, u32 pasid, u64 addr,
int mask = ilog2(__roundup_pow_of_two(npages));
unsigned long align = (1ULL << (VTD_PAGE_SHIFT + mask));

if (WARN_ON_ONCE(!ALIGN(addr, align)))
addr &= ~(align - 1);
if (WARN_ON_ONCE(!IS_ALIGNED(addr, align)))
addr = ALIGN_DOWN(addr, align);

desc.qw0 = QI_EIOTLB_PASID(pasid) |
QI_EIOTLB_DID(did) |
Expand Down
148 changes: 108 additions & 40 deletions drivers/iommu/intel/iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,8 @@ static int domain_update_device_node(struct dmar_domain *domain)
return nid;
}

static void domain_update_iotlb(struct dmar_domain *domain);

/* Some capabilities may be different across iommus */
static void domain_update_iommu_cap(struct dmar_domain *domain)
{
Expand All @@ -744,6 +746,8 @@ static void domain_update_iommu_cap(struct dmar_domain *domain)
domain->domain.geometry.aperture_end = __DOMAIN_MAX_ADDR(domain->gaw - 1);
else
domain->domain.geometry.aperture_end = __DOMAIN_MAX_ADDR(domain->gaw);

domain_update_iotlb(domain);
}

struct context_entry *iommu_context_addr(struct intel_iommu *iommu, u8 bus,
Expand Down Expand Up @@ -1464,17 +1468,22 @@ static void domain_update_iotlb(struct dmar_domain *domain)

assert_spin_locked(&device_domain_lock);

list_for_each_entry(info, &domain->devices, link) {
struct pci_dev *pdev;

if (!info->dev || !dev_is_pci(info->dev))
continue;

pdev = to_pci_dev(info->dev);
if (pdev->ats_enabled) {
list_for_each_entry(info, &domain->devices, link)
if (info->ats_enabled) {
has_iotlb_device = true;
break;
}

if (!has_iotlb_device) {
struct subdev_domain_info *sinfo;

list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
info = get_domain_info(sinfo->pdev);
if (info && info->ats_enabled) {
has_iotlb_device = true;
break;
}
}
}

domain->has_iotlb_device = has_iotlb_device;
Expand Down Expand Up @@ -1555,25 +1564,37 @@ static void iommu_disable_dev_iotlb(struct device_domain_info *info)
#endif
}

static void __iommu_flush_dev_iotlb(struct device_domain_info *info,
u64 addr, unsigned int mask)
{
u16 sid, qdep;

if (!info || !info->ats_enabled)
return;

sid = info->bus << 8 | info->devfn;
qdep = info->ats_qdep;
qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
qdep, addr, mask);
}

static void iommu_flush_dev_iotlb(struct dmar_domain *domain,
u64 addr, unsigned mask)
{
u16 sid, qdep;
unsigned long flags;
struct device_domain_info *info;
struct subdev_domain_info *sinfo;

if (!domain->has_iotlb_device)
return;

spin_lock_irqsave(&device_domain_lock, flags);
list_for_each_entry(info, &domain->devices, link) {
if (!info->ats_enabled)
continue;
list_for_each_entry(info, &domain->devices, link)
__iommu_flush_dev_iotlb(info, addr, mask);

sid = info->bus << 8 | info->devfn;
qdep = info->ats_qdep;
qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
qdep, addr, mask);
list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
info = get_domain_info(sinfo->pdev);
__iommu_flush_dev_iotlb(info, addr, mask);
}
spin_unlock_irqrestore(&device_domain_lock, flags);
}
Expand Down Expand Up @@ -1877,6 +1898,7 @@ static struct dmar_domain *alloc_domain(int flags)
domain->flags |= DOMAIN_FLAG_USE_FIRST_LEVEL;
domain->has_iotlb_device = false;
INIT_LIST_HEAD(&domain->devices);
INIT_LIST_HEAD(&domain->subdevices);

return domain;
}
Expand Down Expand Up @@ -2547,7 +2569,7 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu,
info->iommu = iommu;
info->pasid_table = NULL;
info->auxd_enabled = 0;
INIT_LIST_HEAD(&info->auxiliary_domains);
INIT_LIST_HEAD(&info->subdevices);

if (dev && dev_is_pci(dev)) {
struct pci_dev *pdev = to_pci_dev(info->dev);
Expand Down Expand Up @@ -4475,33 +4497,61 @@ is_aux_domain(struct device *dev, struct iommu_domain *domain)
domain->type == IOMMU_DOMAIN_UNMANAGED;
}

static void auxiliary_link_device(struct dmar_domain *domain,
struct device *dev)
static inline struct subdev_domain_info *
lookup_subdev_info(struct dmar_domain *domain, struct device *dev)
{
struct subdev_domain_info *sinfo;

if (!list_empty(&domain->subdevices)) {
list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
if (sinfo->pdev == dev)
return sinfo;
}
}

return NULL;
}

static int auxiliary_link_device(struct dmar_domain *domain,
struct device *dev)
{
struct device_domain_info *info = get_domain_info(dev);
struct subdev_domain_info *sinfo = lookup_subdev_info(domain, dev);

assert_spin_locked(&device_domain_lock);
if (WARN_ON(!info))
return;
return -EINVAL;

if (!sinfo) {
sinfo = kzalloc(sizeof(*sinfo), GFP_ATOMIC);
sinfo->domain = domain;
sinfo->pdev = dev;
list_add(&sinfo->link_phys, &info->subdevices);
list_add(&sinfo->link_domain, &domain->subdevices);
}

domain->auxd_refcnt++;
list_add(&domain->auxd, &info->auxiliary_domains);
return ++sinfo->users;
}

static void auxiliary_unlink_device(struct dmar_domain *domain,
struct device *dev)
static int auxiliary_unlink_device(struct dmar_domain *domain,
struct device *dev)
{
struct device_domain_info *info = get_domain_info(dev);
struct subdev_domain_info *sinfo = lookup_subdev_info(domain, dev);
int ret;

assert_spin_locked(&device_domain_lock);
if (WARN_ON(!info))
return;
if (WARN_ON(!info || !sinfo || sinfo->users <= 0))
return -EINVAL;

list_del(&domain->auxd);
domain->auxd_refcnt--;
ret = --sinfo->users;
if (!ret) {
list_del(&sinfo->link_phys);
list_del(&sinfo->link_domain);
kfree(sinfo);
}

if (!domain->auxd_refcnt && domain->default_pasid > 0)
ioasid_put(domain->default_pasid);
return ret;
}

static int aux_domain_add_dev(struct dmar_domain *domain,
Expand Down Expand Up @@ -4530,6 +4580,19 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
}

spin_lock_irqsave(&device_domain_lock, flags);
ret = auxiliary_link_device(domain, dev);
if (ret <= 0)
goto link_failed;

/*
* Subdevices from the same physical device can be attached to the
* same domain. For such cases, only the first subdevice attachment
* needs to go through the full steps in this function. So if ret >
* 1, just goto out.
*/
if (ret > 1)
goto out;

/*
* iommu->lock must be held to attach domain to iommu and setup the
* pasid entry for second level translation.
Expand All @@ -4548,10 +4611,9 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
domain->default_pasid);
if (ret)
goto table_failed;
spin_unlock(&iommu->lock);

auxiliary_link_device(domain, dev);

spin_unlock(&iommu->lock);
out:
spin_unlock_irqrestore(&device_domain_lock, flags);

return 0;
Expand All @@ -4560,8 +4622,10 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
domain_detach_iommu(domain, iommu);
attach_failed:
spin_unlock(&iommu->lock);
auxiliary_unlink_device(domain, dev);
link_failed:
spin_unlock_irqrestore(&device_domain_lock, flags);
if (!domain->auxd_refcnt && domain->default_pasid > 0)
if (list_empty(&domain->subdevices) && domain->default_pasid > 0)
ioasid_put(domain->default_pasid);

return ret;
Expand All @@ -4581,14 +4645,18 @@ static void aux_domain_remove_dev(struct dmar_domain *domain,
info = get_domain_info(dev);
iommu = info->iommu;

auxiliary_unlink_device(domain, dev);

spin_lock(&iommu->lock);
intel_pasid_tear_down_entry(iommu, dev, domain->default_pasid, false);
domain_detach_iommu(domain, iommu);
spin_unlock(&iommu->lock);
if (!auxiliary_unlink_device(domain, dev)) {
spin_lock(&iommu->lock);
intel_pasid_tear_down_entry(iommu, dev,
domain->default_pasid, false);
domain_detach_iommu(domain, iommu);
spin_unlock(&iommu->lock);
}

spin_unlock_irqrestore(&device_domain_lock, flags);

if (list_empty(&domain->subdevices) && domain->default_pasid > 0)
ioasid_put(domain->default_pasid);
}

static int prepare_domain_attach_device(struct iommu_domain *domain,
Expand Down
2 changes: 2 additions & 0 deletions drivers/iommu/intel/irq_remapping.c
Original file line number Diff line number Diff line change
Expand Up @@ -1353,6 +1353,8 @@ static int intel_irq_remapping_alloc(struct irq_domain *domain,
irq_data = irq_domain_get_irq_data(domain, virq + i);
irq_cfg = irqd_cfg(irq_data);
if (!irq_data || !irq_cfg) {
if (!i)
kfree(data);
ret = -EINVAL;
goto out_free_data;
}
Expand Down
Loading

0 comments on commit 3e2a590

Please sign in to comment.