From 50917e265ebd2ea33bc34b22b3981f1e88415eae Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 5 Aug 2014 16:38:38 +0200 Subject: iommu/amd: Move struct iommu_dev_data to amd_iommu.c The struct is only used there, so it doesn't need to be in the header file. Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/iommu/amd_iommu.c') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index ecb0109a5360..fe6d7cc715f7 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -87,6 +87,27 @@ int amd_iommu_max_glx_val = -1; static struct dma_map_ops amd_iommu_dma_ops; +/* + * This struct contains device specific data for the IOMMU + */ +struct iommu_dev_data { + struct list_head list; /* For domain->dev_list */ + struct list_head dev_data_list; /* For global dev_data_list */ + struct iommu_dev_data *alias_data;/* The alias dev_data */ + struct protection_domain *domain; /* Domain the device is bound to */ + atomic_t bind; /* Domain attach reference count */ + u16 devid; /* PCI Device ID */ + bool iommu_v2; /* Device can make use of IOMMUv2 */ + bool passthrough; /* Default for device is pt_domain */ + struct { + bool enabled; + int qdep; + } ats; /* ATS state */ + bool pri_tlp; /* PASID TLB required for + PPR completions */ + u32 errata; /* Bitmap for errata to apply */ +}; + /* * general struct to manage commands send to an IOMMU */ -- cgit v1.2.3 From f251e187f2949c690fc13a91df7b873a6b5be671 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 5 Aug 2014 16:48:10 +0200 Subject: iommu/amd: Keep a list of devices in an alias group Some broken devices might use any request-id from the alias group, so we need to set a DTE entry for every device in there. This patch adds creation of those lists. Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/iommu/amd_iommu.c') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index fe6d7cc715f7..8a152564a098 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -93,6 +93,7 @@ static struct dma_map_ops amd_iommu_dma_ops; struct iommu_dev_data { struct list_head list; /* For domain->dev_list */ struct list_head dev_data_list; /* For global dev_data_list */ + struct list_head alias_list; /* Link alias-groups together */ struct iommu_dev_data *alias_data;/* The alias dev_data */ struct protection_domain *domain; /* Domain the device is bound to */ atomic_t bind; /* Domain attach reference count */ @@ -135,6 +136,8 @@ static struct iommu_dev_data *alloc_dev_data(u16 devid) if (!dev_data) return NULL; + INIT_LIST_HEAD(&dev_data->alias_list); + dev_data->devid = devid; atomic_set(&dev_data->bind, 0); @@ -383,6 +386,9 @@ static int iommu_init_device(struct device *dev) return -ENOTSUPP; } dev_data->alias_data = alias_data; + + /* Add device to the alias_list */ + list_add(&dev_data->alias_list, &alias_data->alias_list); } ret = init_iommu_group(dev); -- cgit v1.2.3 From 397111abaaac259afcc48cd2fbfb78f63f27e797 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 5 Aug 2014 17:31:51 +0200 Subject: iommu/amd: Attach and detach complete alias group Change tha device attach and detach semantic to apply to all devices in an alias group. This means all devices in an alias group are now attached and detached at the same time. Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 46 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 26 deletions(-) (limited to 'drivers/iommu/amd_iommu.c') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 8a152564a098..a5e6b0a2de16 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -2149,35 +2149,29 @@ static void do_detach(struct iommu_dev_data *dev_data) static int __attach_device(struct iommu_dev_data *dev_data, struct protection_domain *domain) { + struct iommu_dev_data *head, *entry; int ret; /* lock domain */ spin_lock(&domain->lock); - if (dev_data->alias_data != NULL) { - struct iommu_dev_data *alias_data = dev_data->alias_data; - - /* Some sanity checks */ - ret = -EBUSY; - if (alias_data->domain != NULL && - alias_data->domain != domain) - goto out_unlock; + head = dev_data; - if (dev_data->domain != NULL && - dev_data->domain != domain) - goto out_unlock; + if (head->alias_data != NULL) + head = head->alias_data; - /* Do real assignment */ - if (alias_data->domain == NULL) - do_attach(alias_data, domain); + /* Now we have the root of the alias group, if any */ - atomic_inc(&alias_data->bind); - } + ret = -EBUSY; + if (head->domain != NULL) + goto out_unlock; - if (dev_data->domain == NULL) - do_attach(dev_data, domain); + /* Attach alias group root */ + do_attach(head, domain); - atomic_inc(&dev_data->bind); + /* Attach other devices in the alias group */ + list_for_each_entry(entry, &head->alias_list, alias_list) + do_attach(entry, domain); ret = 0; @@ -2325,6 +2319,7 @@ static int attach_device(struct device *dev, */ static void __detach_device(struct iommu_dev_data *dev_data) { + struct iommu_dev_data *head, *entry; struct protection_domain *domain; unsigned long flags; @@ -2334,15 +2329,14 @@ static void __detach_device(struct iommu_dev_data *dev_data) spin_lock_irqsave(&domain->lock, flags); - if (dev_data->alias_data != NULL) { - struct iommu_dev_data *alias_data = dev_data->alias_data; + head = dev_data; + if (head->alias_data != NULL) + head = head->alias_data; - if (atomic_dec_and_test(&alias_data->bind)) - do_detach(alias_data); - } + list_for_each_entry(entry, &head->alias_list, alias_list) + do_detach(entry); - if (atomic_dec_and_test(&dev_data->bind)) - do_detach(dev_data); + do_detach(head); spin_unlock_irqrestore(&domain->lock, flags); -- cgit v1.2.3 From cafd2545cfd36ef6fee14f9c36870b81d3c98de5 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 5 Aug 2014 17:34:25 +0200 Subject: iommu/amd: Remove device binding reference count This reference count is not used anymore, as all devices in an alias group are now attached and detached together. Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/iommu/amd_iommu.c') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index a5e6b0a2de16..989c1ae03979 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -96,7 +96,6 @@ struct iommu_dev_data { struct list_head alias_list; /* Link alias-groups together */ struct iommu_dev_data *alias_data;/* The alias dev_data */ struct protection_domain *domain; /* Domain the device is bound to */ - atomic_t bind; /* Domain attach reference count */ u16 devid; /* PCI Device ID */ bool iommu_v2; /* Device can make use of IOMMUv2 */ bool passthrough; /* Default for device is pt_domain */ @@ -139,7 +138,6 @@ static struct iommu_dev_data *alloc_dev_data(u16 devid) INIT_LIST_HEAD(&dev_data->alias_list); dev_data->devid = devid; - atomic_set(&dev_data->bind, 0); spin_lock_irqsave(&dev_data_list_lock, flags); list_add_tail(&dev_data->dev_data_list, &dev_data_list); @@ -3179,7 +3177,6 @@ static void cleanup_domain(struct protection_domain *domain) entry = list_first_entry(&domain->dev_list, struct iommu_dev_data, list); __detach_device(entry); - atomic_set(&entry->bind, 0); } write_unlock_irqrestore(&amd_iommu_devtable_lock, flags); -- cgit v1.2.3 From ab636481863a1039f5ca3643cfb04d4ae0c965cd Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Fri, 5 Sep 2014 10:48:21 +0200 Subject: iommu/amd: Convert to iommu_capable() API function Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/iommu/amd_iommu.c') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index ecb0109a5360..7de92768871d 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3384,20 +3384,20 @@ static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom, return paddr; } -static int amd_iommu_domain_has_cap(struct iommu_domain *domain, - unsigned long cap) +static bool amd_iommu_capable(enum iommu_cap cap) { switch (cap) { case IOMMU_CAP_CACHE_COHERENCY: - return 1; + return true; case IOMMU_CAP_INTR_REMAP: - return irq_remapping_enabled; + return (irq_remapping_enabled == 1); } - return 0; + return false; } static const struct iommu_ops amd_iommu_ops = { + .capable = amd_iommu_capable, .domain_init = amd_iommu_domain_init, .domain_destroy = amd_iommu_domain_destroy, .attach_dev = amd_iommu_attach_device, @@ -3405,7 +3405,6 @@ static const struct iommu_ops amd_iommu_ops = { .map = amd_iommu_map, .unmap = amd_iommu_unmap, .iova_to_phys = amd_iommu_iova_to_phys, - .domain_has_cap = amd_iommu_domain_has_cap, .pgsize_bitmap = AMD_IOMMU_PGSIZES, }; -- cgit v1.2.3 From 63eaa75e4362ac7981a7e619196a9c75fd03d717 Mon Sep 17 00:00:00 2001 From: Maurizio Lombardi Date: Thu, 11 Sep 2014 12:28:03 +0200 Subject: amd_iommu: do not dereference a NULL pointer address. under low memory conditions, alloc_pte() may return a NULL pointer. iommu_map_page() does not check it and will panic the system. Signed-off-by: Maurizio Lombardi Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/iommu/amd_iommu.c') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 989c1ae03979..23c5be6a3a1e 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -1393,6 +1393,9 @@ static int iommu_map_page(struct protection_domain *dom, count = PAGE_SIZE_PTE_COUNT(page_size); pte = alloc_pte(dom, bus_addr, page_size, NULL, GFP_KERNEL); + if (!pte) + return -ENOMEM; + for (i = 0; i < count; ++i) if (IOMMU_PTE_PRESENT(pte[i])) return -EBUSY; -- cgit v1.2.3 From 25b11ce2a3607d7c39a2ca121eea0c67c722b34e Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Fri, 19 Sep 2014 10:03:13 -0600 Subject: iommu/amd: Split init_iommu_group() from iommu_init_device() For a PCI device, aliases from the IVRS table won't be populated into dma_alias_devfn until after iommu_init_device() is called on each device. We therefore want to split init_iommu_group() to be called from a separate loop immediately following. Signed-off-by: Alex Williamson Cc: stable@vger.kernel.org # 3.17 Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'drivers/iommu/amd_iommu.c') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 7de92768871d..8c5216633987 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -260,17 +260,13 @@ static bool check_device(struct device *dev) return true; } -static int init_iommu_group(struct device *dev) +static void init_iommu_group(struct device *dev) { struct iommu_group *group; group = iommu_group_get_for_dev(dev); - - if (IS_ERR(group)) - return PTR_ERR(group); - - iommu_group_put(group); - return 0; + if (!IS_ERR(group)) + iommu_group_put(group); } static int __last_alias(struct pci_dev *pdev, u16 alias, void *data) @@ -340,7 +336,6 @@ static int iommu_init_device(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); struct iommu_dev_data *dev_data; u16 alias; - int ret; if (dev->archdata.iommu) return 0; @@ -364,12 +359,6 @@ static int iommu_init_device(struct device *dev) dev_data->alias_data = alias_data; } - ret = init_iommu_group(dev); - if (ret) { - free_dev_data(dev_data); - return ret; - } - if (pci_iommuv2_capable(pdev)) { struct amd_iommu *iommu; @@ -455,6 +444,15 @@ int __init amd_iommu_init_devices(void) goto out_free; } + /* + * Initialize IOMMU groups only after iommu_init_device() has + * had a chance to populate any IVRS defined aliases. + */ + for_each_pci_dev(pdev) { + if (check_device(&pdev->dev)) + init_iommu_group(&pdev->dev); + } + return 0; out_free: @@ -2415,6 +2413,7 @@ static int device_change_notifier(struct notifier_block *nb, case BUS_NOTIFY_ADD_DEVICE: iommu_init_device(dev); + init_iommu_group(dev); /* * dev_data is still NULL and -- cgit v1.2.3 From 5fc24d8cb9d43e0db335137051092da03a6845dc Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Wed, 17 Sep 2014 17:32:19 +0800 Subject: iommu/irq_remapping: Fix the regression of hpet irq remapping Commit 71054d8841b4 ("x86, hpet: Introduce x86_msi_ops.setup_hpet_msi") introduced x86_msi_ops.setup_hpet_msi to setup hpet MSI irq when irq remapping enabled. This caused a regression of hpet MSI irq remapping. Original code flow before commit 71054d8841b4: hpet_setup_msi_irq() arch_setup_hpet_msi() setup_hpet_msi_remapped() remap_ops->setup_hpet_msi() alloc_irte() msi_compose_msg() hpet_msi_write() ... Current code flow after commit 71054d8841b4: hpet_setup_msi_irq() x86_msi.setup_hpet_msi() setup_hpet_msi_remapped() intel_setup_hpet_msi() alloc_irte() Currently, we only call alloc_irte() for hpet MSI, but do not composed and wrote its msg... Signed-off-by: Yijing Wang Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 4 ++-- drivers/iommu/intel_irq_remapping.c | 4 ++-- drivers/iommu/irq_remapping.c | 11 +++++++++-- drivers/iommu/irq_remapping.h | 2 +- 4 files changed, 14 insertions(+), 7 deletions(-) (limited to 'drivers/iommu/amd_iommu.c') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 8c5216633987..2f4f45d8dbfd 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -4233,7 +4233,7 @@ static int msi_setup_irq(struct pci_dev *pdev, unsigned int irq, return 0; } -static int setup_hpet_msi(unsigned int irq, unsigned int id) +static int alloc_hpet_msi(unsigned int irq, unsigned int id) { struct irq_2_irte *irte_info; struct irq_cfg *cfg; @@ -4272,6 +4272,6 @@ struct irq_remap_ops amd_iommu_irq_ops = { .compose_msi_msg = compose_msi_msg, .msi_alloc_irq = msi_alloc_irq, .msi_setup_irq = msi_setup_irq, - .setup_hpet_msi = setup_hpet_msi, + .alloc_hpet_msi = alloc_hpet_msi, }; #endif diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c index 0df41f6264f5..ef5e5dd1311d 100644 --- a/drivers/iommu/intel_irq_remapping.c +++ b/drivers/iommu/intel_irq_remapping.c @@ -1139,7 +1139,7 @@ static int intel_msi_setup_irq(struct pci_dev *pdev, unsigned int irq, return ret; } -static int intel_setup_hpet_msi(unsigned int irq, unsigned int id) +static int intel_alloc_hpet_msi(unsigned int irq, unsigned int id) { int ret = -1; struct intel_iommu *iommu; @@ -1170,5 +1170,5 @@ struct irq_remap_ops intel_irq_remap_ops = { .compose_msi_msg = intel_compose_msi_msg, .msi_alloc_irq = intel_msi_alloc_irq, .msi_setup_irq = intel_msi_setup_irq, - .setup_hpet_msi = intel_setup_hpet_msi, + .alloc_hpet_msi = intel_alloc_hpet_msi, }; diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c index 33c439524080..74a1767c89b5 100644 --- a/drivers/iommu/irq_remapping.c +++ b/drivers/iommu/irq_remapping.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "irq_remapping.h" @@ -345,10 +346,16 @@ static int msi_setup_remapped_irq(struct pci_dev *pdev, unsigned int irq, int setup_hpet_msi_remapped(unsigned int irq, unsigned int id) { - if (!remap_ops || !remap_ops->setup_hpet_msi) + int ret; + + if (!remap_ops || !remap_ops->alloc_hpet_msi) return -ENODEV; - return remap_ops->setup_hpet_msi(irq, id); + ret = remap_ops->alloc_hpet_msi(irq, id); + if (ret) + return -EINVAL; + + return default_setup_hpet_msi(irq, id); } void panic_if_irq_remap(const char *msg) diff --git a/drivers/iommu/irq_remapping.h b/drivers/iommu/irq_remapping.h index 90c4dae5a46b..fde250f86e60 100644 --- a/drivers/iommu/irq_remapping.h +++ b/drivers/iommu/irq_remapping.h @@ -80,7 +80,7 @@ struct irq_remap_ops { int (*msi_setup_irq)(struct pci_dev *, unsigned int, int, int); /* Setup interrupt remapping for an HPET MSI */ - int (*setup_hpet_msi)(unsigned int, unsigned int); + int (*alloc_hpet_msi)(unsigned int, unsigned int); }; extern struct irq_remap_ops intel_irq_remap_ops; -- cgit v1.2.3