diff options
author | Alex Williamson <alex.williamson@redhat.com> | 2012-06-11 01:27:07 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2012-06-12 11:21:42 -0400 |
commit | ad805758c0eb25bce7b2e3b298d63dc62a1bc71c (patch) | |
tree | 031c42d1537e9ee25b7392a652a7799c3914c972 /drivers/pci/quirks.c | |
parent | 12ea6cad1c7d046e21decc18b0e2170c6794dc51 (diff) |
PCI: add ACS validation utility
In a PCI environment, transactions aren't always required to reach
the root bus before being re-routed. Intermediate switches between
an endpoint and the root bus can redirect DMA back downstream before
things like IOMMUs have a chance to intervene. Legacy PCI is always
susceptible to this as it operates on a shared bus. PCIe added a
new capability to describe and control this behavior, Access Control
Services, or ACS.
The utility function pci_acs_enabled() allows us to test the ACS
capabilities of an individual devices against a set of flags while
pci_acs_path_enabled() tests a complete path from a given downstream
device up to the specified upstream device. We also include the
ability to add device specific tests as it's likely we'll see
devices that do not implement ACS, but want to indicate support
for various capabilities in this space.
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Diffstat (limited to 'drivers/pci/quirks.c')
-rw-r--r-- | drivers/pci/quirks.c | 33 |
1 files changed, 33 insertions, 0 deletions
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index acd3956b44bd..27e2c8f4ec73 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c | |||
@@ -3230,3 +3230,36 @@ struct pci_dev *pci_get_dma_source(struct pci_dev *dev) | |||
3230 | 3230 | ||
3231 | return pci_dev_get(dev); | 3231 | return pci_dev_get(dev); |
3232 | } | 3232 | } |
3233 | |||
3234 | static const struct pci_dev_acs_enabled { | ||
3235 | u16 vendor; | ||
3236 | u16 device; | ||
3237 | int (*acs_enabled)(struct pci_dev *dev, u16 acs_flags); | ||
3238 | } pci_dev_acs_enabled[] = { | ||
3239 | { 0 } | ||
3240 | }; | ||
3241 | |||
3242 | int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags) | ||
3243 | { | ||
3244 | const struct pci_dev_acs_enabled *i; | ||
3245 | int ret; | ||
3246 | |||
3247 | /* | ||
3248 | * Allow devices that do not expose standard PCIe ACS capabilities | ||
3249 | * or control to indicate their support here. Multi-function express | ||
3250 | * devices which do not allow internal peer-to-peer between functions, | ||
3251 | * but do not implement PCIe ACS may wish to return true here. | ||
3252 | */ | ||
3253 | for (i = pci_dev_acs_enabled; i->acs_enabled; i++) { | ||
3254 | if ((i->vendor == dev->vendor || | ||
3255 | i->vendor == (u16)PCI_ANY_ID) && | ||
3256 | (i->device == dev->device || | ||
3257 | i->device == (u16)PCI_ANY_ID)) { | ||
3258 | ret = i->acs_enabled(dev, acs_flags); | ||
3259 | if (ret >= 0) | ||
3260 | return ret; | ||
3261 | } | ||
3262 | } | ||
3263 | |||
3264 | return -ENOTTY; | ||
3265 | } | ||