Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #Patch-collection from https://github.com/awilliam/linux-vfio/tree/dma-alias-v4
- #Diff between 3.15-rc6 and his patched 3.15-rc6 tree - successfully patched 3.15.1-STABLE with this.
- #cd linux-3.15.1 && patch -p1 < ../iommupatches.patch # where the latter file is the properly formed patch .
- #Might not be a professional approach, but this is just for my internal use, anyway.
- #I take ABSOLUTELY no credit for it.
- diff -Naur linux-3.15-rc6/drivers/iommu/amd_iommu.c linux-awilliam/drivers/iommu/amd_iommu.c
- --- linux-3.15-rc6/drivers/iommu/amd_iommu.c 2014-05-21 23:42:02.000000000 +0200
- +++ linux-awilliam/drivers/iommu/amd_iommu.c 2014-06-23 23:00:25.000000000 +0200
- @@ -46,7 +46,6 @@
- #include "amd_iommu_proto.h"
- #include "amd_iommu_types.h"
- #include "irq_remapping.h"
- -#include "pci.h"
- #define CMD_SET_TYPE(cmd, t) ((cmd)->data[1] |= ((t) << 28))
- @@ -133,9 +132,6 @@
- list_del(&dev_data->dev_data_list);
- spin_unlock_irqrestore(&dev_data_list_lock, flags);
- - if (dev_data->group)
- - iommu_group_put(dev_data->group);
- -
- kfree(dev_data);
- }
- @@ -264,167 +260,79 @@
- return true;
- }
- -static struct pci_bus *find_hosted_bus(struct pci_bus *bus)
- -{
- - while (!bus->self) {
- - if (!pci_is_root_bus(bus))
- - bus = bus->parent;
- - else
- - return ERR_PTR(-ENODEV);
- - }
- -
- - return bus;
- -}
- -
- -#define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF)
- -
- -static struct pci_dev *get_isolation_root(struct pci_dev *pdev)
- +static int init_iommu_group(struct device *dev)
- {
- - struct pci_dev *dma_pdev = pdev;
- -
- - /* Account for quirked devices */
- - swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev));
- -
- - /*
- - * If it's a multifunction device that does not support our
- - * required ACS flags, add to the same group as lowest numbered
- - * function that also does not suport the required ACS flags.
- - */
- - if (dma_pdev->multifunction &&
- - !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)) {
- - u8 i, slot = PCI_SLOT(dma_pdev->devfn);
- -
- - for (i = 0; i < 8; i++) {
- - struct pci_dev *tmp;
- -
- - tmp = pci_get_slot(dma_pdev->bus, PCI_DEVFN(slot, i));
- - if (!tmp)
- - continue;
- -
- - if (!pci_acs_enabled(tmp, REQ_ACS_FLAGS)) {
- - swap_pci_ref(&dma_pdev, tmp);
- - break;
- - }
- - pci_dev_put(tmp);
- - }
- - }
- + struct iommu_group *group;
- - /*
- - * Devices on the root bus go through the iommu. If that's not us,
- - * find the next upstream device and test ACS up to the root bus.
- - * Finding the next device may require skipping virtual buses.
- - */
- - while (!pci_is_root_bus(dma_pdev->bus)) {
- - struct pci_bus *bus = find_hosted_bus(dma_pdev->bus);
- - if (IS_ERR(bus))
- - break;
- + group = iommu_group_get_for_dev(dev);
- - if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS))
- - break;
- + if (IS_ERR(group))
- + return PTR_ERR(group);
- - swap_pci_ref(&dma_pdev, pci_dev_get(bus->self));
- - }
- -
- - return dma_pdev;
- + iommu_group_put(group);
- + return 0;
- }
- -static int use_pdev_iommu_group(struct pci_dev *pdev, struct device *dev)
- +static int __last_alias(struct pci_dev *pdev, u16 alias, void *data)
- {
- - struct iommu_group *group = iommu_group_get(&pdev->dev);
- - int ret;
- -
- - if (!group) {
- - group = iommu_group_alloc();
- - if (IS_ERR(group))
- - return PTR_ERR(group);
- -
- - WARN_ON(&pdev->dev != dev);
- - }
- -
- - ret = iommu_group_add_device(group, dev);
- - iommu_group_put(group);
- - return ret;
- + *(u16 *)data = alias;
- + return 0;
- }
- -static int use_dev_data_iommu_group(struct iommu_dev_data *dev_data,
- - struct device *dev)
- +static u16 get_alias(struct device *dev)
- {
- - if (!dev_data->group) {
- - struct iommu_group *group = iommu_group_alloc();
- - if (IS_ERR(group))
- - return PTR_ERR(group);
- + struct pci_dev *pdev = to_pci_dev(dev);
- + u16 devid, ivrs_alias, pci_alias;
- - dev_data->group = group;
- - }
- + devid = get_device_id(dev);
- + ivrs_alias = amd_iommu_alias_table[devid];
- + pci_for_each_dma_alias(pdev, __last_alias, &pci_alias);
- - return iommu_group_add_device(dev_data->group, dev);
- -}
- + if (ivrs_alias == pci_alias)
- + return ivrs_alias;
- -static int init_iommu_group(struct device *dev)
- -{
- - struct iommu_dev_data *dev_data;
- - struct iommu_group *group;
- - struct pci_dev *dma_pdev;
- - int ret;
- + /*
- + * DMA alias showdown
- + *
- + * The IVRS is fairly reliable in telling us about aliases, but it
- + * can't know about every screwy device. If we don't have an IVRS
- + * reported alias, use the PCI reported alias. In that case we may
- + * still need to initialize the rlookup and dev_table entries if the
- + * alias is to a non-existent device.
- + */
- + if (ivrs_alias == devid) {
- + if (!amd_iommu_rlookup_table[pci_alias]) {
- + amd_iommu_rlookup_table[pci_alias] =
- + amd_iommu_rlookup_table[devid];
- + memcpy(amd_iommu_dev_table[pci_alias].data,
- + amd_iommu_dev_table[devid].data,
- + sizeof(amd_iommu_dev_table[pci_alias].data));
- + }
- - group = iommu_group_get(dev);
- - if (group) {
- - iommu_group_put(group);
- - return 0;
- + return pci_alias;
- }
- - dev_data = find_dev_data(get_device_id(dev));
- - if (!dev_data)
- - return -ENOMEM;
- -
- - if (dev_data->alias_data) {
- - u16 alias;
- - struct pci_bus *bus;
- -
- - if (dev_data->alias_data->group)
- - goto use_group;
- + pr_info("AMD-Vi: Using IVRS reported alias %02x:%02x.%d "
- + "for device %s[%04x:%04x], kernel reported alias "
- + "%02x:%02x.%d\n", PCI_BUS_NUM(ivrs_alias), PCI_SLOT(ivrs_alias),
- + PCI_FUNC(ivrs_alias), dev_name(dev), pdev->vendor, pdev->device,
- + PCI_BUS_NUM(pci_alias), PCI_SLOT(pci_alias),
- + PCI_FUNC(pci_alias));
- - /*
- - * If the alias device exists, it's effectively just a first
- - * level quirk for finding the DMA source.
- - */
- - alias = amd_iommu_alias_table[dev_data->devid];
- - dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff);
- - if (dma_pdev) {
- - dma_pdev = get_isolation_root(dma_pdev);
- - goto use_pdev;
- - }
- + /*
- + * If we don't have a PCI DMA alias and the IVRS alias is on the same
- + * bus, then the IVRS table may know about a quirk that we don't.
- + */
- + if (pci_alias == devid &&
- + PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) {
- + pdev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
- + pdev->dma_alias_devfn = ivrs_alias & 0xff;
- + pr_info("AMD-Vi: Added PCI DMA alias %02x.%d for %s\n",
- + PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias),
- + dev_name(dev));
- + }
- - /*
- - * If the alias is virtual, try to find a parent device
- - * and test whether the IOMMU group is actualy rooted above
- - * the alias. Be careful to also test the parent device if
- - * we think the alias is the root of the group.
- - */
- - bus = pci_find_bus(0, alias >> 8);
- - if (!bus)
- - goto use_group;
- -
- - bus = find_hosted_bus(bus);
- - if (IS_ERR(bus) || !bus->self)
- - goto use_group;
- -
- - dma_pdev = get_isolation_root(pci_dev_get(bus->self));
- - if (dma_pdev != bus->self || (dma_pdev->multifunction &&
- - !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)))
- - goto use_pdev;
- -
- - pci_dev_put(dma_pdev);
- - goto use_group;
- - }
- -
- - dma_pdev = get_isolation_root(pci_dev_get(to_pci_dev(dev)));
- -use_pdev:
- - ret = use_pdev_iommu_group(dma_pdev, dev);
- - pci_dev_put(dma_pdev);
- - return ret;
- -use_group:
- - return use_dev_data_iommu_group(dev_data->alias_data, dev);
- + return ivrs_alias;
- }
- static int iommu_init_device(struct device *dev)
- @@ -441,7 +349,8 @@
- if (!dev_data)
- return -ENOMEM;
- - alias = amd_iommu_alias_table[dev_data->devid];
- + alias = get_alias(dev);
- +
- if (alias != dev_data->devid) {
- struct iommu_dev_data *alias_data;
- @@ -489,12 +398,19 @@
- static void iommu_uninit_device(struct device *dev)
- {
- + struct iommu_dev_data *dev_data = search_dev_data(get_device_id(dev));
- +
- + if (!dev_data)
- + return;
- +
- iommu_group_remove_device(dev);
- + /* Unlink from alias, it may change if another device is re-plugged */
- + dev_data->alias_data = NULL;
- +
- /*
- - * Nothing to do here - we keep dev_data around for unplugged devices
- - * and reuse it when the device is re-plugged - not doing so would
- - * introduce a ton of races.
- + * We keep dev_data around for unplugged devices and reuse it when the
- + * device is re-plugged - not doing so would introduce a ton of races.
- */
- }
- diff -Naur linux-3.15-rc6/drivers/iommu/amd_iommu_types.h linux-awilliam/drivers/iommu/amd_iommu_types.h
- --- linux-3.15-rc6/drivers/iommu/amd_iommu_types.h 2014-05-21 23:42:02.000000000 +0200
- +++ linux-awilliam/drivers/iommu/amd_iommu_types.h 2014-06-23 23:00:25.000000000 +0200
- @@ -432,7 +432,6 @@
- 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 */
- - struct iommu_group *group; /* IOMMU group for virtual aliases */
- u16 devid; /* PCI Device ID */
- bool iommu_v2; /* Device can make use of IOMMUv2 */
- bool passthrough; /* Default for device is pt_domain */
- diff -Naur linux-3.15-rc6/drivers/iommu/fsl_pamu_domain.c linux-awilliam/drivers/iommu/fsl_pamu_domain.c
- --- linux-3.15-rc6/drivers/iommu/fsl_pamu_domain.c 2014-05-21 23:42:02.000000000 +0200
- +++ linux-awilliam/drivers/iommu/fsl_pamu_domain.c 2014-06-23 23:00:25.000000000 +0200
- @@ -38,7 +38,6 @@
- #include <sysdev/fsl_pci.h>
- #include "fsl_pamu_domain.h"
- -#include "pci.h"
- /*
- * Global spinlock that needs to be held while
- @@ -892,8 +891,6 @@
- return ret;
- }
- -#define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF)
- -
- static struct iommu_group *get_device_iommu_group(struct device *dev)
- {
- struct iommu_group *group;
- @@ -950,74 +947,13 @@
- struct pci_controller *pci_ctl;
- bool pci_endpt_partioning;
- struct iommu_group *group = NULL;
- - struct pci_dev *bridge, *dma_pdev = NULL;
- pci_ctl = pci_bus_to_host(pdev->bus);
- pci_endpt_partioning = check_pci_ctl_endpt_part(pci_ctl);
- /* We can partition PCIe devices so assign device group to the device */
- if (pci_endpt_partioning) {
- - bridge = pci_find_upstream_pcie_bridge(pdev);
- - if (bridge) {
- - if (pci_is_pcie(bridge))
- - dma_pdev = pci_get_domain_bus_and_slot(
- - pci_domain_nr(pdev->bus),
- - bridge->subordinate->number, 0);
- - if (!dma_pdev)
- - dma_pdev = pci_dev_get(bridge);
- - } else
- - dma_pdev = pci_dev_get(pdev);
- -
- - /* Account for quirked devices */
- - swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev));
- -
- - /*
- - * If it's a multifunction device that does not support our
- - * required ACS flags, add to the same group as lowest numbered
- - * function that also does not suport the required ACS flags.
- - */
- - if (dma_pdev->multifunction &&
- - !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)) {
- - u8 i, slot = PCI_SLOT(dma_pdev->devfn);
- -
- - for (i = 0; i < 8; i++) {
- - struct pci_dev *tmp;
- -
- - tmp = pci_get_slot(dma_pdev->bus, PCI_DEVFN(slot, i));
- - if (!tmp)
- - continue;
- -
- - if (!pci_acs_enabled(tmp, REQ_ACS_FLAGS)) {
- - swap_pci_ref(&dma_pdev, tmp);
- - break;
- - }
- - pci_dev_put(tmp);
- - }
- - }
- -
- - /*
- - * Devices on the root bus go through the iommu. If that's not us,
- - * find the next upstream device and test ACS up to the root bus.
- - * Finding the next device may require skipping virtual buses.
- - */
- - while (!pci_is_root_bus(dma_pdev->bus)) {
- - struct pci_bus *bus = dma_pdev->bus;
- -
- - while (!bus->self) {
- - if (!pci_is_root_bus(bus))
- - bus = bus->parent;
- - else
- - goto root_bus;
- - }
- -
- - if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS))
- - break;
- -
- - swap_pci_ref(&dma_pdev, pci_dev_get(bus->self));
- - }
- + group = iommu_group_get_for_dev(&pdev->dev);
- -root_bus:
- - group = get_device_iommu_group(&dma_pdev->dev);
- - pci_dev_put(dma_pdev);
- /*
- * PCIe controller is not a paritionable entity
- * free the controller device iommu_group.
- diff -Naur linux-3.15-rc6/drivers/iommu/intel-iommu.c linux-awilliam/drivers/iommu/intel-iommu.c
- --- linux-3.15-rc6/drivers/iommu/intel-iommu.c 2014-05-21 23:42:02.000000000 +0200
- +++ linux-awilliam/drivers/iommu/intel-iommu.c 2014-06-23 23:00:25.000000000 +0200
- @@ -44,7 +44,6 @@
- #include <asm/iommu.h>
- #include "irq_remapping.h"
- -#include "pci.h"
- #define ROOT_SIZE VTD_PAGE_SIZE
- #define CONTEXT_SIZE VTD_PAGE_SIZE
- @@ -1841,54 +1840,56 @@
- return 0;
- }
- +struct domain_context_mapping_data {
- + struct dmar_domain *domain;
- + struct intel_iommu *iommu;
- + int translation;
- +};
- +
- +static int domain_context_mapping_cb(struct pci_dev *pdev,
- + u16 alias, void *opaque)
- +{
- + struct domain_context_mapping_data *data = opaque;
- +
- + return domain_context_mapping_one(data->domain, data->iommu,
- + PCI_BUS_NUM(alias), alias & 0xff,
- + data->translation);
- +}
- +
- static int
- domain_context_mapping(struct dmar_domain *domain, struct device *dev,
- int translation)
- {
- - int ret;
- - struct pci_dev *pdev, *tmp, *parent;
- struct intel_iommu *iommu;
- u8 bus, devfn;
- + struct domain_context_mapping_data data;
- iommu = device_to_iommu(dev, &bus, &devfn);
- if (!iommu)
- return -ENODEV;
- - ret = domain_context_mapping_one(domain, iommu, bus, devfn,
- - translation);
- - if (ret || !dev_is_pci(dev))
- - return ret;
- -
- - /* dependent device mapping */
- - pdev = to_pci_dev(dev);
- - tmp = pci_find_upstream_pcie_bridge(pdev);
- - if (!tmp)
- - return 0;
- - /* Secondary interface's bus number and devfn 0 */
- - parent = pdev->bus->self;
- - while (parent != tmp) {
- - ret = domain_context_mapping_one(domain, iommu,
- - parent->bus->number,
- - parent->devfn, translation);
- - if (ret)
- - return ret;
- - parent = parent->bus->self;
- - }
- - if (pci_is_pcie(tmp)) /* this is a PCIe-to-PCI bridge */
- - return domain_context_mapping_one(domain, iommu,
- - tmp->subordinate->number, 0,
- - translation);
- - else /* this is a legacy PCI bridge */
- - return domain_context_mapping_one(domain, iommu,
- - tmp->bus->number,
- - tmp->devfn,
- + if (!dev_is_pci(dev))
- + return domain_context_mapping_one(domain, iommu, bus, devfn,
- translation);
- +
- + data.domain = domain;
- + data.iommu = iommu;
- + data.translation = translation;
- +
- + return pci_for_each_dma_alias(to_pci_dev(dev),
- + &domain_context_mapping_cb, &data);
- +}
- +
- +static int domain_context_mapped_cb(struct pci_dev *pdev,
- + u16 alias, void *opaque)
- +{
- + struct intel_iommu *iommu = opaque;
- +
- + return !device_context_mapped(iommu, PCI_BUS_NUM(alias), alias & 0xff);
- }
- static int domain_context_mapped(struct device *dev)
- {
- - int ret;
- - struct pci_dev *pdev, *tmp, *parent;
- struct intel_iommu *iommu;
- u8 bus, devfn;
- @@ -1896,30 +1897,11 @@
- if (!iommu)
- return -ENODEV;
- - ret = device_context_mapped(iommu, bus, devfn);
- - if (!ret || !dev_is_pci(dev))
- - return ret;
- + if (!dev_is_pci(dev))
- + return device_context_mapped(iommu, bus, devfn);
- - /* dependent device mapping */
- - pdev = to_pci_dev(dev);
- - tmp = pci_find_upstream_pcie_bridge(pdev);
- - if (!tmp)
- - return ret;
- - /* Secondary interface's bus number and devfn 0 */
- - parent = pdev->bus->self;
- - while (parent != tmp) {
- - ret = device_context_mapped(iommu, parent->bus->number,
- - parent->devfn);
- - if (!ret)
- - return ret;
- - parent = parent->bus->self;
- - }
- - if (pci_is_pcie(tmp))
- - return device_context_mapped(iommu, tmp->subordinate->number,
- - 0);
- - else
- - return device_context_mapped(iommu, tmp->bus->number,
- - tmp->devfn);
- + return !pci_for_each_dma_alias(to_pci_dev(dev),
- + domain_context_mapped_cb, iommu);
- }
- /* Returns a number of VTD pages, but aligned to MM page size */
- @@ -2208,79 +2190,86 @@
- return domain;
- }
- +static int get_last_alias(struct pci_dev *pdev, u16 alias, void *opaque)
- +{
- + *(u16 *)opaque = alias;
- + return 0;
- +}
- +
- /* domain is initialized */
- static struct dmar_domain *get_domain_for_dev(struct device *dev, int gaw)
- {
- - struct dmar_domain *domain, *free = NULL;
- - struct intel_iommu *iommu = NULL;
- + struct dmar_domain *domain, *tmp;
- + struct intel_iommu *iommu;
- struct device_domain_info *info;
- - struct pci_dev *dev_tmp = NULL;
- + u16 dma_alias;
- unsigned long flags;
- - u8 bus, devfn, bridge_bus, bridge_devfn;
- + u8 bus, devfn;
- domain = find_domain(dev);
- if (domain)
- return domain;
- + iommu = device_to_iommu(dev, &bus, &devfn);
- + if (!iommu)
- + return NULL;
- +
- if (dev_is_pci(dev)) {
- struct pci_dev *pdev = to_pci_dev(dev);
- - u16 segment;
- - segment = pci_domain_nr(pdev->bus);
- - dev_tmp = pci_find_upstream_pcie_bridge(pdev);
- - if (dev_tmp) {
- - if (pci_is_pcie(dev_tmp)) {
- - bridge_bus = dev_tmp->subordinate->number;
- - bridge_devfn = 0;
- - } else {
- - bridge_bus = dev_tmp->bus->number;
- - bridge_devfn = dev_tmp->devfn;
- - }
- - spin_lock_irqsave(&device_domain_lock, flags);
- - info = dmar_search_domain_by_dev_info(segment,
- - bridge_bus,
- - bridge_devfn);
- - if (info) {
- - iommu = info->iommu;
- - domain = info->domain;
- - }
- - spin_unlock_irqrestore(&device_domain_lock, flags);
- - /* pcie-pci bridge already has a domain, uses it */
- - if (info)
- - goto found_domain;
- + pci_for_each_dma_alias(pdev, get_last_alias, &dma_alias);
- +
- + spin_lock_irqsave(&device_domain_lock, flags);
- + info = dmar_search_domain_by_dev_info(pci_domain_nr(pdev->bus),
- + PCI_BUS_NUM(dma_alias),
- + dma_alias & 0xff);
- + if (info) {
- + iommu = info->iommu;
- + domain = info->domain;
- }
- - }
- + spin_unlock_irqrestore(&device_domain_lock, flags);
- - iommu = device_to_iommu(dev, &bus, &devfn);
- - if (!iommu)
- - goto error;
- + /* DMA alias already has a domain, uses it */
- + if (info)
- + goto found_domain;
- + }
- /* Allocate and initialize new domain for the device */
- domain = alloc_domain(false);
- if (!domain)
- - goto error;
- + return NULL;
- +
- if (iommu_attach_domain(domain, iommu)) {
- free_domain_mem(domain);
- - domain = NULL;
- - goto error;
- + return NULL;
- + }
- +
- + if (domain_init(domain, gaw)) {
- + domain_exit(domain);
- + return NULL;
- }
- - free = domain;
- - if (domain_init(domain, gaw))
- - goto error;
- - /* register pcie-to-pci device */
- - if (dev_tmp) {
- - domain = dmar_insert_dev_info(iommu, bridge_bus, bridge_devfn,
- - NULL, domain);
- + /* register PCI DMA alias device */
- + if (dev_is_pci(dev)) {
- + tmp = dmar_insert_dev_info(iommu, PCI_BUS_NUM(dma_alias),
- + dma_alias & 0xff, NULL, domain);
- +
- + if (!tmp || tmp != domain) {
- + domain_exit(domain);
- + domain = tmp;
- + }
- +
- if (!domain)
- - goto error;
- + return NULL;
- }
- found_domain:
- - domain = dmar_insert_dev_info(iommu, bus, devfn, dev, domain);
- -error:
- - if (free != domain)
- - domain_exit(free);
- + tmp = dmar_insert_dev_info(iommu, bus, devfn, dev, domain);
- +
- + if (!tmp || tmp != domain) {
- + domain_exit(domain);
- + domain = tmp;
- + }
- return domain;
- }
- @@ -4030,33 +4019,27 @@
- return ret;
- }
- +static int iommu_detach_dev_cb(struct pci_dev *pdev, u16 alias, void *opaque)
- +{
- + struct intel_iommu *iommu = opaque;
- +
- + iommu_detach_dev(iommu, PCI_BUS_NUM(alias), alias & 0xff);
- + return 0;
- +}
- +
- +/*
- + * NB - intel-iommu lacks any sort of reference counting for the users of
- + * dependent devices. If multiple endpoints have intersecting dependent
- + * devices, unbinding the driver from any one of them will possibly leave
- + * the others unable to operate.
- + */
- static void iommu_detach_dependent_devices(struct intel_iommu *iommu,
- struct device *dev)
- {
- - struct pci_dev *tmp, *parent, *pdev;
- -
- if (!iommu || !dev || !dev_is_pci(dev))
- return;
- - pdev = to_pci_dev(dev);
- -
- - /* dependent device detach */
- - tmp = pci_find_upstream_pcie_bridge(pdev);
- - /* Secondary interface's bus number and devfn 0 */
- - if (tmp) {
- - parent = pdev->bus->self;
- - while (parent != tmp) {
- - iommu_detach_dev(iommu, parent->bus->number,
- - parent->devfn);
- - parent = parent->bus->self;
- - }
- - if (pci_is_pcie(tmp)) /* this is a PCIe-to-PCI bridge */
- - iommu_detach_dev(iommu,
- - tmp->subordinate->number, 0);
- - else /* this is a legacy PCI bridge */
- - iommu_detach_dev(iommu, tmp->bus->number,
- - tmp->devfn);
- - }
- + pci_for_each_dma_alias(to_pci_dev(dev), &iommu_detach_dev_cb, iommu);
- }
- static void domain_remove_one_dev_info(struct dmar_domain *domain,
- @@ -4359,91 +4342,21 @@
- return 0;
- }
- -#define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF)
- -
- static int intel_iommu_add_device(struct device *dev)
- {
- - struct pci_dev *pdev = to_pci_dev(dev);
- - struct pci_dev *bridge, *dma_pdev = NULL;
- struct iommu_group *group;
- - int ret;
- u8 bus, devfn;
- if (!device_to_iommu(dev, &bus, &devfn))
- return -ENODEV;
- - bridge = pci_find_upstream_pcie_bridge(pdev);
- - if (bridge) {
- - if (pci_is_pcie(bridge))
- - dma_pdev = pci_get_domain_bus_and_slot(
- - pci_domain_nr(pdev->bus),
- - bridge->subordinate->number, 0);
- - if (!dma_pdev)
- - dma_pdev = pci_dev_get(bridge);
- - } else
- - dma_pdev = pci_dev_get(pdev);
- -
- - /* Account for quirked devices */
- - swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev));
- -
- - /*
- - * If it's a multifunction device that does not support our
- - * required ACS flags, add to the same group as lowest numbered
- - * function that also does not suport the required ACS flags.
- - */
- - if (dma_pdev->multifunction &&
- - !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)) {
- - u8 i, slot = PCI_SLOT(dma_pdev->devfn);
- -
- - for (i = 0; i < 8; i++) {
- - struct pci_dev *tmp;
- -
- - tmp = pci_get_slot(dma_pdev->bus, PCI_DEVFN(slot, i));
- - if (!tmp)
- - continue;
- + group = iommu_group_get_for_dev(dev);
- - if (!pci_acs_enabled(tmp, REQ_ACS_FLAGS)) {
- - swap_pci_ref(&dma_pdev, tmp);
- - break;
- - }
- - pci_dev_put(tmp);
- - }
- - }
- -
- - /*
- - * Devices on the root bus go through the iommu. If that's not us,
- - * find the next upstream device and test ACS up to the root bus.
- - * Finding the next device may require skipping virtual buses.
- - */
- - while (!pci_is_root_bus(dma_pdev->bus)) {
- - struct pci_bus *bus = dma_pdev->bus;
- -
- - while (!bus->self) {
- - if (!pci_is_root_bus(bus))
- - bus = bus->parent;
- - else
- - goto root_bus;
- - }
- -
- - if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS))
- - break;
- -
- - swap_pci_ref(&dma_pdev, pci_dev_get(bus->self));
- - }
- -
- -root_bus:
- - group = iommu_group_get(&dma_pdev->dev);
- - pci_dev_put(dma_pdev);
- - if (!group) {
- - group = iommu_group_alloc();
- - if (IS_ERR(group))
- - return PTR_ERR(group);
- - }
- -
- - ret = iommu_group_add_device(group, dev);
- + if (IS_ERR(group))
- + return PTR_ERR(group);
- iommu_group_put(group);
- - return ret;
- + return 0;
- }
- static void intel_iommu_remove_device(struct device *dev)
- diff -Naur linux-3.15-rc6/drivers/iommu/intel_irq_remapping.c linux-awilliam/drivers/iommu/intel_irq_remapping.c
- --- linux-3.15-rc6/drivers/iommu/intel_irq_remapping.c 2014-05-21 23:42:02.000000000 +0200
- +++ linux-awilliam/drivers/iommu/intel_irq_remapping.c 2014-06-23 23:00:25.000000000 +0200
- @@ -369,29 +369,52 @@
- return 0;
- }
- +struct set_msi_sid_data {
- + struct pci_dev *pdev;
- + u16 alias;
- +};
- +
- +static int set_msi_sid_cb(struct pci_dev *pdev, u16 alias, void *opaque)
- +{
- + struct set_msi_sid_data *data = opaque;
- +
- + data->pdev = pdev;
- + data->alias = alias;
- +
- + return 0;
- +}
- +
- static int set_msi_sid(struct irte *irte, struct pci_dev *dev)
- {
- - struct pci_dev *bridge;
- + struct set_msi_sid_data data;
- if (!irte || !dev)
- return -1;
- - /* PCIe device or Root Complex integrated PCI device */
- - if (pci_is_pcie(dev) || !dev->bus->parent) {
- - set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
- - (dev->bus->number << 8) | dev->devfn);
- - return 0;
- - }
- + pci_for_each_dma_alias(dev, set_msi_sid_cb, &data);
- - bridge = pci_find_upstream_pcie_bridge(dev);
- - if (bridge) {
- - if (pci_is_pcie(bridge))/* this is a PCIe-to-PCI/PCIX bridge */
- - set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16,
- - (bridge->bus->number << 8) | dev->bus->number);
- - else /* this is a legacy PCI bridge */
- - set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
- - (bridge->bus->number << 8) | bridge->devfn);
- - }
- + /*
- + * DMA alias provides us with a PCI device and alias. The only case
- + * where the it will return an alias on a different bus than the
- + * device is the case of a PCIe-to-PCI bridge, where the alias is for
- + * the subordinate bus. In this case we can only verify the bus.
- + *
- + * If the alias device is on a different bus than our source device
- + * then we have a topology based alias, use it.
- + *
- + * Otherwise, the alias is for a device DMA quirk and we cannot
- + * assume that MSI uses the same requester ID. Therefore use the
- + * original device.
- + */
- + if (PCI_BUS_NUM(data.alias) != data.pdev->bus->number)
- + set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16,
- + PCI_DEVID(PCI_BUS_NUM(data.alias),
- + dev->bus->number));
- + else if (data.pdev->bus->number != dev->bus->number)
- + set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16, data.alias);
- + else
- + set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
- + PCI_DEVID(dev->bus->number, dev->devfn));
- return 0;
- }
- diff -Naur linux-3.15-rc6/drivers/iommu/iommu.c linux-awilliam/drivers/iommu/iommu.c
- --- linux-3.15-rc6/drivers/iommu/iommu.c 2014-05-21 23:42:02.000000000 +0200
- +++ linux-awilliam/drivers/iommu/iommu.c 2014-06-23 23:00:25.000000000 +0200
- @@ -29,6 +29,7 @@
- #include <linux/idr.h>
- #include <linux/notifier.h>
- #include <linux/err.h>
- +#include <linux/pci.h>
- #include <trace/events/iommu.h>
- static struct kset *iommu_group_kset;
- @@ -514,6 +515,186 @@
- }
- EXPORT_SYMBOL_GPL(iommu_group_id);
- +/*
- + * To consider a PCI device isolated, we require ACS to support Source
- + * Validation, Request Redirection, Completer Redirection, and Upstream
- + * Forwarding. This effectively means that devices cannot spoof their
- + * requester ID, requests and completions cannot be redirected, and all
- + * transactions are forwarded upstream, even as it passes through a
- + * bridge where the target device is downstream.
- + */
- +#define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF)
- +
- +struct group_for_pci_data {
- + struct pci_dev *pdev;
- + struct iommu_group *group;
- +};
- +
- +/*
- + * DMA alias iterator callback, return the last seen device. Stop and return
- + * the IOMMU group if we find one along the way.
- + */
- +static int get_pci_alias_or_group(struct pci_dev *pdev, u16 alias, void *opaque)
- +{
- + struct group_for_pci_data *data = opaque;
- +
- + data->pdev = pdev;
- + data->group = iommu_group_get(&pdev->dev);
- +
- + return data->group != NULL;
- +}
- +
- +/*
- + * Use standard PCI bus topology, isolation features, and DMA alias quirks
- + * to find or create an IOMMU group for a device.
- + */
- +static struct iommu_group *iommu_group_get_for_pci_dev(struct pci_dev *pdev)
- +{
- + struct group_for_pci_data data;
- + struct pci_bus *bus;
- + struct iommu_group *group = NULL;
- + struct pci_dev *tmp;
- +
- + /*
- + * Find the upstream DMA alias for the device. A device must not
- + * be aliased due to topology in order to have its own IOMMU group.
- + * If we find an alias along the way that already belongs to a
- + * group, use it.
- + */
- + if (pci_for_each_dma_alias(pdev, get_pci_alias_or_group, &data))
- + return data.group;
- +
- + pdev = data.pdev;
- +
- + /*
- + * Continue upstream from the point of minimum IOMMU granularity
- + * due to aliases to the point where devices are protected from
- + * peer-to-peer DMA by PCI ACS. Again, if we find an existing
- + * group, use it.
- + */
- + for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) {
- + if (!bus->self)
- + continue;
- +
- + if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS))
- + break;
- +
- + pdev = bus->self;
- +
- + group = iommu_group_get(&pdev->dev);
- + if (group)
- + return group;
- + }
- +
- + /*
- + * Next we need to consider DMA alias quirks. If one device aliases
- + * to another, they should be grouped together. It's theoretically
- + * possible that aliases could create chains of devices where each
- + * device aliases another device. If we then factor in multifunction
- + * ACS grouping requirements, each alias could incorporate a new slot
- + * with multiple functions, each with aliases. This is all extremely
- + * unlikely as DMA alias quirks are typically only used for PCIe
- + * devices where we usually have a single slot per bus. Furthermore,
- + * the alias quirk is usually to another function within the slot
- + * (and ACS multifunction is not supported) or to a different slot
- + * that doesn't physically exist. The likely scenario is therefore
- + * that everything on the bus gets grouped together. To reduce the
- + * problem space, share the IOMMU group for all devices on the bus
- + * if a DMA alias quirk is present on the bus.
- + */
- + tmp = NULL;
- + for_each_pci_dev(tmp) {
- + if (tmp->bus != pdev->bus ||
- + !(tmp->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN))
- + continue;
- +
- + pci_dev_put(tmp);
- + tmp = NULL;
- +
- + /* We have an alias quirk, search for an existing group */
- + for_each_pci_dev(tmp) {
- + struct iommu_group *group_tmp;
- + if (tmp->bus != pdev->bus)
- + continue;
- +
- + group_tmp = iommu_group_get(&tmp->dev);
- + if (!group) {
- + group = group_tmp;
- + continue;
- + }
- +
- + if (group_tmp) {
- + WARN_ON(group != group_tmp);
- + iommu_group_put(group_tmp);
- + }
- + }
- +
- + return group ? group : iommu_group_alloc();
- + }
- +
- + /*
- + * Non-multifunction devices or multifunction devices supporting
- + * ACS get their own group.
- + */
- + if (!pdev->multifunction || pci_acs_enabled(pdev, REQ_ACS_FLAGS))
- + return iommu_group_alloc();
- +
- + /*
- + * Multifunction devices not supporting ACS share a group with other
- + * similar devices in the same slot.
- + */
- + tmp = NULL;
- + for_each_pci_dev(tmp) {
- + if (tmp == pdev || tmp->bus != pdev->bus ||
- + PCI_SLOT(tmp->devfn) != PCI_SLOT(pdev->devfn) ||
- + pci_acs_enabled(tmp, REQ_ACS_FLAGS))
- + continue;
- +
- + group = iommu_group_get(&tmp->dev);
- + if (group) {
- + pci_dev_put(tmp);
- + return group;
- + }
- + }
- +
- + /* No shared group found, allocate new */
- + return iommu_group_alloc();
- +}
- +
- +/**
- + * iommu_group_get_for_dev - Find or create the IOMMU group for a device
- + * @dev: target device
- + *
- + * This function is intended to be called by IOMMU drivers and extended to
- + * support common, bus-defined algorithms when determining or creating the
- + * IOMMU group for a device. On success, the caller will hold a reference
- + * to the returned IOMMU group, which will already include the provided
- + * device. The reference should be released with iommu_group_put().
- + */
- +struct iommu_group *iommu_group_get_for_dev(struct device *dev)
- +{
- + struct iommu_group *group = ERR_PTR(-EIO);
- + int ret;
- +
- + group = iommu_group_get(dev);
- + if (group)
- + return group;
- +
- + if (dev_is_pci(dev))
- + group = iommu_group_get_for_pci_dev(to_pci_dev(dev));
- +
- + if (IS_ERR(group))
- + return group;
- +
- + ret = iommu_group_add_device(group, dev);
- + if (ret) {
- + iommu_group_put(group);
- + return ERR_PTR(ret);
- + }
- +
- + return group;
- +}
- +
- static int add_iommu_group(struct device *dev, void *data)
- {
- struct iommu_ops *ops = data;
- diff -Naur linux-3.15-rc6/drivers/iommu/pci.h linux-awilliam/drivers/iommu/pci.h
- --- linux-3.15-rc6/drivers/iommu/pci.h 2014-05-21 23:42:02.000000000 +0200
- +++ linux-awilliam/drivers/iommu/pci.h 1970-01-01 01:00:00.000000000 +0100
- @@ -1,29 +0,0 @@
- -/*
- - * This program is free software; you can redistribute it and/or modify
- - * it under the terms of the GNU General Public License, version 2, as
- - * published by the Free Software Foundation.
- - *
- - * This program is distributed in the hope that it will be useful,
- - * but WITHOUT ANY WARRANTY; without even the implied warranty of
- - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- - * GNU General Public License for more details.
- - *
- - * You should have received a copy of the GNU General Public License
- - * along with this program; if not, write to the Free Software
- - * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- - *
- - * Copyright (C) 2013 Red Hat, Inc.
- - * Copyright (C) 2013 Freescale Semiconductor, Inc.
- - *
- - */
- -#ifndef __IOMMU_PCI_H
- -#define __IOMMU_PCI_H
- -
- -/* Helper function for swapping pci device reference */
- -static inline void swap_pci_ref(struct pci_dev **from, struct pci_dev *to)
- -{
- - pci_dev_put(*from);
- - *from = to;
- -}
- -
- -#endif /* __IOMMU_PCI_H */
- diff -Naur linux-3.15-rc6/drivers/pci/quirks.c linux-awilliam/drivers/pci/quirks.c
- --- linux-3.15-rc6/drivers/pci/quirks.c 2014-05-21 23:42:02.000000000 +0200
- +++ linux-awilliam/drivers/pci/quirks.c 2014-06-23 23:00:25.000000000 +0200
- @@ -3333,58 +3333,82 @@
- return -ENOTTY;
- }
- -static struct pci_dev *pci_func_0_dma_source(struct pci_dev *dev)
- +static void quirk_dma_func0_alias(struct pci_dev *dev)
- {
- - if (!PCI_FUNC(dev->devfn))
- - return pci_dev_get(dev);
- -
- - return pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
- + if (PCI_FUNC(dev->devfn) != 0) {
- + dev->dma_alias_devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 0);
- + dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
- + }
- }
- -static const struct pci_dev_dma_source {
- - u16 vendor;
- - u16 device;
- - struct pci_dev *(*dma_source)(struct pci_dev *dev);
- -} pci_dev_dma_source[] = {
- - /*
- - * https://bugzilla.redhat.com/show_bug.cgi?id=605888
- - *
- - * Some Ricoh devices use the function 0 source ID for DMA on
- - * other functions of a multifunction device. The DMA devices
- - * is therefore function 0, which will have implications of the
- - * iommu grouping of these devices.
- - */
- - { PCI_VENDOR_ID_RICOH, 0xe822, pci_func_0_dma_source },
- - { PCI_VENDOR_ID_RICOH, 0xe230, pci_func_0_dma_source },
- - { PCI_VENDOR_ID_RICOH, 0xe832, pci_func_0_dma_source },
- - { PCI_VENDOR_ID_RICOH, 0xe476, pci_func_0_dma_source },
- - { 0 }
- -};
- -
- /*
- - * IOMMUs with isolation capabilities need to be programmed with the
- - * correct source ID of a device. In most cases, the source ID matches
- - * the device doing the DMA, but sometimes hardware is broken and will
- - * tag the DMA as being sourced from a different device. This function
- - * allows that translation. Note that the reference count of the
- - * returned device is incremented on all paths.
- + * https://bugzilla.redhat.com/show_bug.cgi?id=605888
- + *
- + * Some Ricoh devices use function 0 as the PCIe requester ID for DMA.
- */
- -struct pci_dev *pci_get_dma_source(struct pci_dev *dev)
- -{
- - const struct pci_dev_dma_source *i;
- +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RICOH, 0xe832, quirk_dma_func0_alias);
- +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RICOH, 0xe476, quirk_dma_func0_alias);
- - for (i = pci_dev_dma_source; i->dma_source; i++) {
- - if ((i->vendor == dev->vendor ||
- - i->vendor == (u16)PCI_ANY_ID) &&
- - (i->device == dev->device ||
- - i->device == (u16)PCI_ANY_ID))
- - return i->dma_source(dev);
- +static void quirk_dma_func1_alias(struct pci_dev *dev)
- +{
- + if (PCI_FUNC(dev->devfn) != 1) {
- + dev->dma_alias_devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 1);
- + dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
- }
- -
- - return pci_dev_get(dev);
- }
- /*
- + * Marvell 88SE9123 uses function 1 as the requester ID for DMA. In some
- + * SKUs function 1 is present and is a legacy IDE controller, in other
- + * SKUs this function is not present, making this a ghost requester.
- + * https://bugzilla.kernel.org/show_bug.cgi?id=42679
- + */
- +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9123,
- + quirk_dma_func1_alias);
- +/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c14 */
- +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9130,
- + quirk_dma_func1_alias);
- +/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c47 + c57 */
- +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9172,
- + quirk_dma_func1_alias);
- +/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c59 */
- +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x917a,
- + quirk_dma_func1_alias);
- +/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c46 */
- +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x91a0,
- + quirk_dma_func1_alias);
- +/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c49 */
- +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9230,
- + quirk_dma_func1_alias);
- +/* https://bugs.gentoo.org/show_bug.cgi?id=497630 */
- +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_JMICRON,
- + PCI_DEVICE_ID_JMICRON_JMB388_ESD,
- + quirk_dma_func1_alias);
- +
- +/*
- + * A few PCIe-to-PCI bridges fail to expose a PCIe capability, resulting in
- + * using the wrong DMA alias for the device. Some of these devices can be
- + * used as either forward or reverse bridges, so we need to test whether the
- + * device is operating in the correct mode. We could probably apply this
- + * quirk to PCI_ANY_ID, but for now we'll just use known offenders. The test
- + * is for a non-root, non-PCIe bridge where the upstream device is PCIe and
- + * is not a PCIe-to-PCI bridge, then @pdev is actually a PCIe-to-PCI bridge.
- + */
- +static void quirk_use_pcie_bridge_dma_alias(struct pci_dev *pdev)
- +{
- + if (!pci_is_root_bus(pdev->bus) &&
- + pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
- + !pci_is_pcie(pdev) && pci_is_pcie(pdev->bus->self) &&
- + pci_pcie_type(pdev->bus->self) != PCI_EXP_TYPE_PCI_BRIDGE)
- + pdev->dev_flags |= PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS;
- +}
- +/* ASM1083/1085, https://bugzilla.kernel.org/show_bug.cgi?id=44881#c46 */
- +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ASMEDIA, 0x1080,
- + quirk_use_pcie_bridge_dma_alias);
- +/* Tundra 8113, https://bugzilla.kernel.org/show_bug.cgi?id=44881#c43 */
- +DECLARE_PCI_FIXUP_HEADER(0x10e3, 0x8113, quirk_use_pcie_bridge_dma_alias);
- +
- +/*
- * AMD has indicated that the devices below do not support peer-to-peer
- * in any system where they are found in the southbridge with an AMD
- * IOMMU in the system. Multifunction devices that do not support
- diff -Naur linux-3.15-rc6/drivers/pci/search.c linux-awilliam/drivers/pci/search.c
- --- linux-3.15-rc6/drivers/pci/search.c 2014-05-21 23:42:02.000000000 +0200
- +++ linux-awilliam/drivers/pci/search.c 2014-06-23 23:00:25.000000000 +0200
- @@ -18,38 +18,90 @@
- EXPORT_SYMBOL_GPL(pci_bus_sem);
- /*
- - * find the upstream PCIe-to-PCI bridge of a PCI device
- - * if the device is PCIE, return NULL
- - * if the device isn't connected to a PCIe bridge (that is its parent is a
- - * legacy PCI bridge and the bridge is directly connected to bus 0), return its
- - * parent
- + * pci_for_each_dma_alias - Iterate over DMA aliases for a device
- + * @pdev: starting downstream device
- + * @fn: function to call for each alias
- + * @data: opaque data to pass to @fn
- + *
- + * Starting @pdev, walk up the bus calling @fn for each possible alias
- + * of @pdev at the root bus.
- */
- -struct pci_dev *
- -pci_find_upstream_pcie_bridge(struct pci_dev *pdev)
- -{
- - struct pci_dev *tmp = NULL;
- +int pci_for_each_dma_alias(struct pci_dev *pdev,
- + int (*fn)(struct pci_dev *pdev,
- + u16 alias, void *data), void *data)
- +{
- + struct pci_bus *bus;
- + int ret;
- +
- + ret = fn(pdev, PCI_DEVID(pdev->bus->number, pdev->devfn), data);
- + if (ret)
- + return ret;
- +
- + /*
- + * If the device is broken and uses an alias requester ID for
- + * DMA, iterate over that too.
- + */
- + if (unlikely(pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN)) {
- + ret = fn(pdev, PCI_DEVID(pdev->bus->number,
- + pdev->dma_alias_devfn), data);
- + if (ret)
- + return ret;
- + }
- - if (pci_is_pcie(pdev))
- - return NULL;
- - while (1) {
- - if (pci_is_root_bus(pdev->bus))
- - break;
- - pdev = pdev->bus->self;
- - /* a p2p bridge */
- - if (!pci_is_pcie(pdev)) {
- - tmp = pdev;
- + for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) {
- + struct pci_dev *tmp;
- +
- + /* Skip virtual buses */
- + if (!bus->self)
- continue;
- +
- + tmp = bus->self;
- +
- + /*
- + * PCIe-to-PCI/X bridges alias transactions from downstream
- + * devices using the subordinate bus number (PCI Express to
- + * PCI/PCI-X Bridge Spec, rev 1.0, sec 2.3). For all cases
- + * where the upstream bus is PCI/X we alias to the bridge
- + * (there are various conditions in the previous reference
- + * where the bridge may take ownership of transactions, even
- + * when the secondary interface is PCI-X).
- + */
- + if (pci_is_pcie(tmp)) {
- + switch (pci_pcie_type(tmp)) {
- + case PCI_EXP_TYPE_ROOT_PORT:
- + case PCI_EXP_TYPE_UPSTREAM:
- + case PCI_EXP_TYPE_DOWNSTREAM:
- + continue;
- + case PCI_EXP_TYPE_PCI_BRIDGE:
- + ret = fn(tmp,
- + PCI_DEVID(tmp->subordinate->number,
- + PCI_DEVFN(0, 0)), data);
- + if (ret)
- + return ret;
- + continue;
- + case PCI_EXP_TYPE_PCIE_BRIDGE:
- + ret = fn(tmp,
- + PCI_DEVID(tmp->bus->number,
- + tmp->devfn), data);
- + if (ret)
- + return ret;
- + continue;
- + }
- + } else {
- + if (tmp->dev_flags & PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS)
- + ret = fn(tmp,
- + PCI_DEVID(tmp->subordinate->number,
- + PCI_DEVFN(0, 0)), data);
- + else
- + ret = fn(tmp,
- + PCI_DEVID(tmp->bus->number,
- + tmp->devfn), data);
- + if (ret)
- + return ret;
- }
- - /* PCI device should connect to a PCIe bridge */
- - if (pci_pcie_type(pdev) != PCI_EXP_TYPE_PCI_BRIDGE) {
- - /* Busted hardware? */
- - WARN_ON_ONCE(1);
- - return NULL;
- - }
- - return pdev;
- }
- - return tmp;
- + return ret;
- }
- static struct pci_bus *pci_do_find_bus(struct pci_bus *bus, unsigned char busnr)
- diff -Naur linux-3.15-rc6/include/linux/iommu.h linux-awilliam/include/linux/iommu.h
- --- linux-3.15-rc6/include/linux/iommu.h 2014-05-21 23:42:02.000000000 +0200
- +++ linux-awilliam/include/linux/iommu.h 2014-06-23 23:00:25.000000000 +0200
- @@ -181,6 +181,7 @@
- extern int iommu_group_unregister_notifier(struct iommu_group *group,
- struct notifier_block *nb);
- extern int iommu_group_id(struct iommu_group *group);
- +extern struct iommu_group *iommu_group_get_for_dev(struct device *dev);
- extern int iommu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr,
- void *data);
- diff -Naur linux-3.15-rc6/include/linux/pci.h linux-awilliam/include/linux/pci.h
- --- linux-3.15-rc6/include/linux/pci.h 2014-05-21 23:42:02.000000000 +0200
- +++ linux-awilliam/include/linux/pci.h 2014-06-23 23:00:25.000000000 +0200
- @@ -164,13 +164,17 @@
- /* INTX_DISABLE in PCI_COMMAND register disables MSI
- * generation too.
- */
- - PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG = (__force pci_dev_flags_t) 1,
- + PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG = (__force pci_dev_flags_t) (1 << 0),
- /* Device configuration is irrevocably lost if disabled into D3 */
- - PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) 2,
- + PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) (1 << 1),
- /* Provide indication device is assigned by a Virtual Machine Manager */
- - PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) 4,
- + PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) (1 << 2),
- /* Flag for quirk use to store if quirk-specific ACS is enabled */
- - PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) 8,
- + PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) (1 << 3),
- + /* Flag to indicate the device uses dma_alias_devfn */
- + PCI_DEV_FLAGS_DMA_ALIAS_DEVFN = (__force pci_dev_flags_t) (1 << 4),
- + /* Use a PCIe-to-PCI bridge alias even if !pci_is_pcie */
- + PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS = (__force pci_dev_flags_t) (1 << 5),
- };
- enum pci_irq_reroute_variant {
- @@ -268,6 +272,7 @@
- u8 rom_base_reg; /* which config register controls the ROM */
- u8 pin; /* which interrupt pin this device uses */
- u16 pcie_flags_reg; /* cached PCIe Capabilities Register */
- + u8 dma_alias_devfn;/* devfn of DMA alias, if any */
- struct pci_driver *driver; /* which driver has allocated this device */
- u64 dma_mask; /* Mask of the bits of bus address this
- @@ -1528,16 +1533,11 @@
- #ifdef CONFIG_PCI_QUIRKS
- void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev);
- -struct pci_dev *pci_get_dma_source(struct pci_dev *dev);
- int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
- void pci_dev_specific_enable_acs(struct pci_dev *dev);
- #else
- static inline void pci_fixup_device(enum pci_fixup_pass pass,
- struct pci_dev *dev) { }
- -static inline struct pci_dev *pci_get_dma_source(struct pci_dev *dev)
- -{
- - return pci_dev_get(dev);
- -}
- static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
- u16 acs_flags)
- {
- @@ -1795,15 +1795,8 @@
- }
- #endif
- -/**
- - * pci_find_upstream_pcie_bridge - find upstream PCIe-to-PCI bridge of a device
- - * @pdev: the PCI device
- - *
- - * if the device is PCIE, return NULL
- - * if the device isn't connected to a PCIe bridge (that is its parent is a
- - * legacy PCI bridge and the bridge is directly connected to bus 0), return its
- - * parent
- - */
- -struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev);
- +int pci_for_each_dma_alias(struct pci_dev *pdev,
- + int (*fn)(struct pci_dev *pdev,
- + u16 alias, void *data), void *data);
- #endif /* LINUX_PCI_H */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement