Merge branch 'pci/peer-to-peer'
authorBjorn Helgaas <bhelgaas@google.com>
Wed, 15 Aug 2018 19:58:58 +0000 (14:58 -0500)
committerBjorn Helgaas <bhelgaas@google.com>
Wed, 15 Aug 2018 19:58:58 +0000 (14:58 -0500)
  - Add "pci=disable_acs_redir=" parameter to disable ACS redirection for
    peer-to-peer DMA support (we don't have the peer-to-peer support yet;
    this is just one piece) (Logan Gunthorpe)

* pci/peer-to-peer:
  PCI: Add ACS Redirect disable quirk for Intel Sunrise Point
  PCI: Add device-specific ACS Redirect disable infrastructure
  PCI: Convert device-specific ACS quirks from NULL termination to ARRAY_SIZE
  PCI: Add "pci=disable_acs_redir=" parameter for peer-to-peer support
  PCI: Allow specifying devices using a base bus and path of devfns
  PCI: Make specifying PCI devices in kernel parameters reusable
  PCI: Hide ACS quirk declarations inside PCI core

1  2 
drivers/pci/pci.c
drivers/pci/pci.h
drivers/pci/quirks.c
include/linux/pci.h

diff --combined drivers/pci/pci.c
@@@ -23,6 -23,7 +23,6 @@@
  #include <linux/string.h>
  #include <linux/log2.h>
  #include <linux/logic_pio.h>
 -#include <linux/pci-aspm.h>
  #include <linux/pm_wakeup.h>
  #include <linux/interrupt.h>
  #include <linux/device.h>
@@@ -190,6 -191,168 +190,168 @@@ void __iomem *pci_ioremap_wc_bar(struc
  EXPORT_SYMBOL_GPL(pci_ioremap_wc_bar);
  #endif
  
+ /**
+  * pci_dev_str_match_path - test if a path string matches a device
+  * @dev:    the PCI device to test
+  * @p:      string to match the device against
+  * @endptr: pointer to the string after the match
+  *
+  * Test if a string (typically from a kernel parameter) formatted as a
+  * path of device/function addresses matches a PCI device. The string must
+  * be of the form:
+  *
+  *   [<domain>:]<bus>:<device>.<func>[/<device>.<func>]*
+  *
+  * A path for a device can be obtained using 'lspci -t'.  Using a path
+  * is more robust against bus renumbering than using only a single bus,
+  * device and function address.
+  *
+  * Returns 1 if the string matches the device, 0 if it does not and
+  * a negative error code if it fails to parse the string.
+  */
+ static int pci_dev_str_match_path(struct pci_dev *dev, const char *path,
+                                 const char **endptr)
+ {
+       int ret;
+       int seg, bus, slot, func;
+       char *wpath, *p;
+       char end;
+       *endptr = strchrnul(path, ';');
+       wpath = kmemdup_nul(path, *endptr - path, GFP_KERNEL);
+       if (!wpath)
+               return -ENOMEM;
+       while (1) {
+               p = strrchr(wpath, '/');
+               if (!p)
+                       break;
+               ret = sscanf(p, "/%x.%x%c", &slot, &func, &end);
+               if (ret != 2) {
+                       ret = -EINVAL;
+                       goto free_and_exit;
+               }
+               if (dev->devfn != PCI_DEVFN(slot, func)) {
+                       ret = 0;
+                       goto free_and_exit;
+               }
+               /*
+                * Note: we don't need to get a reference to the upstream
+                * bridge because we hold a reference to the top level
+                * device which should hold a reference to the bridge,
+                * and so on.
+                */
+               dev = pci_upstream_bridge(dev);
+               if (!dev) {
+                       ret = 0;
+                       goto free_and_exit;
+               }
+               *p = 0;
+       }
+       ret = sscanf(wpath, "%x:%x:%x.%x%c", &seg, &bus, &slot,
+                    &func, &end);
+       if (ret != 4) {
+               seg = 0;
+               ret = sscanf(wpath, "%x:%x.%x%c", &bus, &slot, &func, &end);
+               if (ret != 3) {
+                       ret = -EINVAL;
+                       goto free_and_exit;
+               }
+       }
+       ret = (seg == pci_domain_nr(dev->bus) &&
+              bus == dev->bus->number &&
+              dev->devfn == PCI_DEVFN(slot, func));
+ free_and_exit:
+       kfree(wpath);
+       return ret;
+ }
+ /**
+  * pci_dev_str_match - test if a string matches a device
+  * @dev:    the PCI device to test
+  * @p:      string to match the device against
+  * @endptr: pointer to the string after the match
+  *
+  * Test if a string (typically from a kernel parameter) matches a specified
+  * PCI device. The string may be of one of the following formats:
+  *
+  *   [<domain>:]<bus>:<device>.<func>[/<device>.<func>]*
+  *   pci:<vendor>:<device>[:<subvendor>:<subdevice>]
+  *
+  * The first format specifies a PCI bus/device/function address which
+  * may change if new hardware is inserted, if motherboard firmware changes,
+  * or due to changes caused in kernel parameters. If the domain is
+  * left unspecified, it is taken to be 0.  In order to be robust against
+  * bus renumbering issues, a path of PCI device/function numbers may be used
+  * to address the specific device.  The path for a device can be determined
+  * through the use of 'lspci -t'.
+  *
+  * The second format matches devices using IDs in the configuration
+  * space which may match multiple devices in the system. A value of 0
+  * for any field will match all devices. (Note: this differs from
+  * in-kernel code that uses PCI_ANY_ID which is ~0; this is for
+  * legacy reasons and convenience so users don't have to specify
+  * FFFFFFFFs on the command line.)
+  *
+  * Returns 1 if the string matches the device, 0 if it does not and
+  * a negative error code if the string cannot be parsed.
+  */
+ static int pci_dev_str_match(struct pci_dev *dev, const char *p,
+                            const char **endptr)
+ {
+       int ret;
+       int count;
+       unsigned short vendor, device, subsystem_vendor, subsystem_device;
+       if (strncmp(p, "pci:", 4) == 0) {
+               /* PCI vendor/device (subvendor/subdevice) IDs are specified */
+               p += 4;
+               ret = sscanf(p, "%hx:%hx:%hx:%hx%n", &vendor, &device,
+                            &subsystem_vendor, &subsystem_device, &count);
+               if (ret != 4) {
+                       ret = sscanf(p, "%hx:%hx%n", &vendor, &device, &count);
+                       if (ret != 2)
+                               return -EINVAL;
+                       subsystem_vendor = 0;
+                       subsystem_device = 0;
+               }
+               p += count;
+               if ((!vendor || vendor == dev->vendor) &&
+                   (!device || device == dev->device) &&
+                   (!subsystem_vendor ||
+                           subsystem_vendor == dev->subsystem_vendor) &&
+                   (!subsystem_device ||
+                           subsystem_device == dev->subsystem_device))
+                       goto found;
+       } else {
+               /*
+                * PCI Bus, Device, Function IDs are specified
+                *  (optionally, may include a path of devfns following it)
+                */
+               ret = pci_dev_str_match_path(dev, p, &p);
+               if (ret < 0)
+                       return ret;
+               else if (ret)
+                       goto found;
+       }
+       *endptr = p;
+       return 0;
+ found:
+       *endptr = p;
+       return 1;
+ }
  
  static int __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn,
                                   u8 pos, int cap, int *ttl)
@@@ -2044,7 -2207,6 +2206,7 @@@ static pci_power_t pci_target_state(str
                case PCI_D2:
                        if (pci_no_d1d2(dev))
                                break;
 +                      /* else: fall through */
                default:
                        target_state = state;
                }
@@@ -2290,7 -2452,7 +2452,7 @@@ void pci_config_pm_runtime_put(struct p
   * @bridge: Bridge to check
   *
   * This function checks if it is possible to move the bridge to D3.
 - * Currently we only allow D3 for recent enough PCIe ports.
 + * Currently we only allow D3 for recent enough PCIe ports and Thunderbolt.
   */
  bool pci_bridge_d3_possible(struct pci_dev *bridge)
  {
                        return false;
  
                /*
 -               * Hotplug interrupts cannot be delivered if the link is down,
 -               * so parents of a hotplug port must stay awake. In addition,
 -               * hotplug ports handled by firmware in System Management Mode
 +               * Hotplug ports handled by firmware in System Management Mode
                 * may not be put into D3 by the OS (Thunderbolt on non-Macs).
 -               * For simplicity, disallow in general for now.
                 */
 -              if (bridge->is_hotplug_bridge)
 +              if (bridge->is_hotplug_bridge && !pciehp_is_native(bridge))
                        return false;
  
                if (pci_bridge_d3_force)
                        return true;
  
 +              /* Even the oldest 2010 Thunderbolt controller supports D3. */
 +              if (bridge->is_thunderbolt)
 +                      return true;
 +
 +              /*
 +               * Hotplug ports handled natively by the OS were not validated
 +               * by vendors for runtime D3 at least until 2018 because there
 +               * was no OS support.
 +               */
 +              if (bridge->is_hotplug_bridge)
 +                      return false;
 +
                /*
                 * It should be safe to put PCIe ports from 2015 or newer
                 * to D3.
@@@ -2829,6 -2982,66 +2991,66 @@@ void pci_request_acs(void
        pci_acs_enable = 1;
  }
  
+ static const char *disable_acs_redir_param;
+ /**
+  * pci_disable_acs_redir - disable ACS redirect capabilities
+  * @dev: the PCI device
+  *
+  * For only devices specified in the disable_acs_redir parameter.
+  */
+ static void pci_disable_acs_redir(struct pci_dev *dev)
+ {
+       int ret = 0;
+       const char *p;
+       int pos;
+       u16 ctrl;
+       if (!disable_acs_redir_param)
+               return;
+       p = disable_acs_redir_param;
+       while (*p) {
+               ret = pci_dev_str_match(dev, p, &p);
+               if (ret < 0) {
+                       pr_info_once("PCI: Can't parse disable_acs_redir parameter: %s\n",
+                                    disable_acs_redir_param);
+                       break;
+               } else if (ret == 1) {
+                       /* Found a match */
+                       break;
+               }
+               if (*p != ';' && *p != ',') {
+                       /* End of param or invalid format */
+                       break;
+               }
+               p++;
+       }
+       if (ret != 1)
+               return;
+       if (!pci_dev_specific_disable_acs_redir(dev))
+               return;
+       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
+       if (!pos) {
+               pci_warn(dev, "cannot disable ACS redirect for this hardware as it does not have ACS capabilities\n");
+               return;
+       }
+       pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl);
+       /* P2P Request & Completion Redirect */
+       ctrl &= ~(PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC);
+       pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
+       pci_info(dev, "disabled ACS redirect\n");
+ }
  /**
   * pci_std_enable_acs - enable ACS on devices using standard ACS capabilites
   * @dev: the PCI device
@@@ -2868,12 -3081,22 +3090,22 @@@ static void pci_std_enable_acs(struct p
  void pci_enable_acs(struct pci_dev *dev)
  {
        if (!pci_acs_enable)
-               return;
+               goto disable_acs_redir;
  
        if (!pci_dev_specific_enable_acs(dev))
-               return;
+               goto disable_acs_redir;
  
        pci_std_enable_acs(dev);
+ disable_acs_redir:
+       /*
+        * Note: pci_disable_acs_redir() must be called even if ACS was not
+        * enabled by the kernel because it may have been enabled by
+        * platform firmware.  So if we are told to disable it, we should
+        * always disable it after setting the kernel's default
+        * preferences.
+        */
+       pci_disable_acs_redir(dev);
  }
  
  static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags)
@@@ -3588,44 -3811,6 +3820,44 @@@ void pci_unmap_iospace(struct resource 
  }
  EXPORT_SYMBOL(pci_unmap_iospace);
  
 +static void devm_pci_unmap_iospace(struct device *dev, void *ptr)
 +{
 +      struct resource **res = ptr;
 +
 +      pci_unmap_iospace(*res);
 +}
 +
 +/**
 + * devm_pci_remap_iospace - Managed pci_remap_iospace()
 + * @dev: Generic device to remap IO address for
 + * @res: Resource describing the I/O space
 + * @phys_addr: physical address of range to be mapped
 + *
 + * Managed pci_remap_iospace().  Map is automatically unmapped on driver
 + * detach.
 + */
 +int devm_pci_remap_iospace(struct device *dev, const struct resource *res,
 +                         phys_addr_t phys_addr)
 +{
 +      const struct resource **ptr;
 +      int error;
 +
 +      ptr = devres_alloc(devm_pci_unmap_iospace, sizeof(*ptr), GFP_KERNEL);
 +      if (!ptr)
 +              return -ENOMEM;
 +
 +      error = pci_remap_iospace(res, phys_addr);
 +      if (error) {
 +              devres_free(ptr);
 +      } else  {
 +              *ptr = res;
 +              devres_add(dev, ptr);
 +      }
 +
 +      return error;
 +}
 +EXPORT_SYMBOL(devm_pci_remap_iospace);
 +
  /**
   * devm_pci_remap_cfgspace - Managed pci_remap_cfgspace()
   * @dev: Generic device to remap IO address for
@@@ -5311,16 -5496,14 +5543,16 @@@ u32 pcie_bandwidth_capable(struct pci_d
  }
  
  /**
 - * pcie_print_link_status - Report the PCI device's link speed and width
 + * __pcie_print_link_status - Report the PCI device's link speed and width
   * @dev: PCI device to query
 + * @verbose: Print info even when enough bandwidth is available
   *
 - * Report the available bandwidth at the device.  If this is less than the
 - * device is capable of, report the device's maximum possible bandwidth and
 - * the upstream link that limits its performance to less than that.
 + * If the available bandwidth at the device is less than the device is
 + * capable of, report the device's maximum possible bandwidth and the
 + * upstream link that limits its performance.  If @verbose, always print
 + * the available bandwidth, even if the device isn't constrained.
   */
 -void pcie_print_link_status(struct pci_dev *dev)
 +void __pcie_print_link_status(struct pci_dev *dev, bool verbose)
  {
        enum pcie_link_width width, width_cap;
        enum pci_bus_speed speed, speed_cap;
        bw_cap = pcie_bandwidth_capable(dev, &speed_cap, &width_cap);
        bw_avail = pcie_bandwidth_available(dev, &limiting_dev, &speed, &width);
  
 -      if (bw_avail >= bw_cap)
 +      if (bw_avail >= bw_cap && verbose)
                pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth (%s x%d link)\n",
                         bw_cap / 1000, bw_cap % 1000,
                         PCIE_SPEED2STR(speed_cap), width_cap);
 -      else
 +      else if (bw_avail < bw_cap)
                pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth, limited by %s x%d link at %s (capable of %u.%03u Gb/s with %s x%d link)\n",
                         bw_avail / 1000, bw_avail % 1000,
                         PCIE_SPEED2STR(speed), width,
                         bw_cap / 1000, bw_cap % 1000,
                         PCIE_SPEED2STR(speed_cap), width_cap);
  }
 +
 +/**
 + * pcie_print_link_status - Report the PCI device's link speed and width
 + * @dev: PCI device to query
 + *
 + * Report the available bandwidth at the device.
 + */
 +void pcie_print_link_status(struct pci_dev *dev)
 +{
 +      __pcie_print_link_status(dev, true);
 +}
  EXPORT_SYMBOL(pcie_print_link_status);
  
  /**
@@@ -5514,10 -5686,10 +5746,10 @@@ static DEFINE_SPINLOCK(resource_alignme
  static resource_size_t pci_specified_resource_alignment(struct pci_dev *dev,
                                                        bool *resize)
  {
-       int seg, bus, slot, func, align_order, count;
-       unsigned short vendor, device, subsystem_vendor, subsystem_device;
+       int align_order, count;
        resource_size_t align = pcibios_default_alignment();
-       char *p;
+       const char *p;
+       int ret;
  
        spin_lock(&resource_alignment_lock);
        p = resource_alignment_param;
                } else {
                        align_order = -1;
                }
-               if (strncmp(p, "pci:", 4) == 0) {
-                       /* PCI vendor/device (subvendor/subdevice) ids are specified */
-                       p += 4;
-                       if (sscanf(p, "%hx:%hx:%hx:%hx%n",
-                               &vendor, &device, &subsystem_vendor, &subsystem_device, &count) != 4) {
-                               if (sscanf(p, "%hx:%hx%n", &vendor, &device, &count) != 2) {
-                                       printk(KERN_ERR "PCI: Can't parse resource_alignment parameter: pci:%s\n",
-                                               p);
-                                       break;
-                               }
-                               subsystem_vendor = subsystem_device = 0;
-                       }
-                       p += count;
-                       if ((!vendor || (vendor == dev->vendor)) &&
-                               (!device || (device == dev->device)) &&
-                               (!subsystem_vendor || (subsystem_vendor == dev->subsystem_vendor)) &&
-                               (!subsystem_device || (subsystem_device == dev->subsystem_device))) {
-                               *resize = true;
-                               if (align_order == -1)
-                                       align = PAGE_SIZE;
-                               else
-                                       align = 1 << align_order;
-                               /* Found */
-                               break;
-                       }
-               }
-               else {
-                       if (sscanf(p, "%x:%x:%x.%x%n",
-                               &seg, &bus, &slot, &func, &count) != 4) {
-                               seg = 0;
-                               if (sscanf(p, "%x:%x.%x%n",
-                                               &bus, &slot, &func, &count) != 3) {
-                                       /* Invalid format */
-                                       printk(KERN_ERR "PCI: Can't parse resource_alignment parameter: %s\n",
-                                               p);
-                                       break;
-                               }
-                       }
-                       p += count;
-                       if (seg == pci_domain_nr(dev->bus) &&
-                               bus == dev->bus->number &&
-                               slot == PCI_SLOT(dev->devfn) &&
-                               func == PCI_FUNC(dev->devfn)) {
-                               *resize = true;
-                               if (align_order == -1)
-                                       align = PAGE_SIZE;
-                               else
-                                       align = 1 << align_order;
-                               /* Found */
-                               break;
-                       }
+               ret = pci_dev_str_match(dev, p, &p);
+               if (ret == 1) {
+                       *resize = true;
+                       if (align_order == -1)
+                               align = PAGE_SIZE;
+                       else
+                               align = 1 << align_order;
+                       break;
+               } else if (ret < 0) {
+                       pr_err("PCI: Can't parse resource_alignment parameter: %s\n",
+                              p);
+                       break;
                }
                if (*p != ';' && *p != ',') {
                        /* End of param or invalid format */
                        break;
@@@ -5901,6 -6036,8 +6096,8 @@@ static int __init pci_setup(char *str
                                pcie_bus_config = PCIE_BUS_PEER2PEER;
                        } else if (!strncmp(str, "pcie_scan_all", 13)) {
                                pci_add_flags(PCI_SCAN_ALL_PCIE_DEVS);
+                       } else if (!strncmp(str, "disable_acs_redir=", 18)) {
+                               disable_acs_redir_param = str + 18;
                        } else {
                                printk(KERN_ERR "PCI: Unknown option `%s'\n",
                                                str);
diff --combined drivers/pci/pci.h
@@@ -225,10 -225,6 +225,10 @@@ enum pci_bar_type 
  int pci_configure_extended_tags(struct pci_dev *dev, void *ign);
  bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
                                int crs_timeout);
 +bool pci_bus_generic_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
 +                                      int crs_timeout);
 +int pci_idt_bus_quirk(struct pci_bus *bus, int devfn, u32 *pl, int crs_timeout);
 +
  int pci_setup_device(struct pci_dev *dev);
  int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
                    struct resource *res, unsigned int reg);
@@@ -263,7 -259,6 +263,7 @@@ enum pci_bus_speed pcie_get_speed_cap(s
  enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev);
  u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed,
                           enum pcie_link_width *width);
 +void __pcie_print_link_status(struct pci_dev *dev, bool verbose);
  
  /* Single Root I/O Virtualization */
  struct pci_sriov {
  
  /* pci_dev priv_flags */
  #define PCI_DEV_DISCONNECTED 0
 +#define PCI_DEV_ADDED 1
  
  static inline int pci_dev_set_disconnected(struct pci_dev *dev, void *unused)
  {
@@@ -306,44 -300,6 +306,44 @@@ static inline bool pci_dev_is_disconnec
        return test_bit(PCI_DEV_DISCONNECTED, &dev->priv_flags);
  }
  
 +static inline void pci_dev_assign_added(struct pci_dev *dev, bool added)
 +{
 +      assign_bit(PCI_DEV_ADDED, &dev->priv_flags, added);
 +}
 +
 +static inline bool pci_dev_is_added(const struct pci_dev *dev)
 +{
 +      return test_bit(PCI_DEV_ADDED, &dev->priv_flags);
 +}
 +
 +#ifdef CONFIG_PCIEAER
 +#include <linux/aer.h>
 +
 +#define AER_MAX_MULTI_ERR_DEVICES     5       /* Not likely to have more */
 +
 +struct aer_err_info {
 +      struct pci_dev *dev[AER_MAX_MULTI_ERR_DEVICES];
 +      int error_dev_num;
 +
 +      unsigned int id:16;
 +
 +      unsigned int severity:2;        /* 0:NONFATAL | 1:FATAL | 2:COR */
 +      unsigned int __pad1:5;
 +      unsigned int multi_error_valid:1;
 +
 +      unsigned int first_error:5;
 +      unsigned int __pad2:2;
 +      unsigned int tlp_header_valid:1;
 +
 +      unsigned int status;            /* COR/UNCOR Error Status */
 +      unsigned int mask;              /* COR/UNCOR Error Mask */
 +      struct aer_header_log_regs tlp; /* TLP Header */
 +};
 +
 +int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info);
 +void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
 +#endif        /* CONFIG_PCIEAER */
 +
  #ifdef CONFIG_PCI_ATS
  void pci_restore_ats_state(struct pci_dev *dev);
  #else
@@@ -355,7 -311,6 +355,7 @@@ static inline void pci_restore_ats_stat
  #ifdef CONFIG_PCI_IOV
  int pci_iov_init(struct pci_dev *dev);
  void pci_iov_release(struct pci_dev *dev);
 +void pci_iov_remove(struct pci_dev *dev);
  void pci_iov_update_resource(struct pci_dev *dev, int resno);
  resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno);
  void pci_restore_iov_state(struct pci_dev *dev);
@@@ -368,9 -323,6 +368,9 @@@ static inline int pci_iov_init(struct p
  }
  static inline void pci_iov_release(struct pci_dev *dev)
  
 +{
 +}
 +static inline void pci_iov_remove(struct pci_dev *dev)
  {
  }
  static inline void pci_restore_iov_state(struct pci_dev *dev)
@@@ -400,6 -352,25 +400,25 @@@ static inline resource_size_t pci_resou
  }
  
  void pci_enable_acs(struct pci_dev *dev);
+ #ifdef CONFIG_PCI_QUIRKS
+ int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
+ int pci_dev_specific_enable_acs(struct pci_dev *dev);
+ int pci_dev_specific_disable_acs_redir(struct pci_dev *dev);
+ #else
+ static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
+                                              u16 acs_flags)
+ {
+       return -ENOTTY;
+ }
+ static inline int pci_dev_specific_enable_acs(struct pci_dev *dev)
+ {
+       return -ENOTTY;
+ }
+ static inline int pci_dev_specific_disable_acs_redir(struct pci_dev *dev)
+ {
+       return -ENOTTY;
+ }
+ #endif
  
  /* PCI error reporting and recovery */
  void pcie_do_fatal_recovery(struct pci_dev *dev, u32 service);
@@@ -500,19 -471,4 +519,19 @@@ static inline int devm_of_pci_get_host_
  }
  #endif
  
 +#ifdef CONFIG_PCIEAER
 +void pci_no_aer(void);
 +void pci_aer_init(struct pci_dev *dev);
 +void pci_aer_exit(struct pci_dev *dev);
 +extern const struct attribute_group aer_stats_attr_group;
 +void pci_aer_clear_fatal_status(struct pci_dev *dev);
 +void pci_aer_clear_device_status(struct pci_dev *dev);
 +#else
 +static inline void pci_no_aer(void) { }
 +static inline int pci_aer_init(struct pci_dev *d) { return -ENODEV; }
 +static inline void pci_aer_exit(struct pci_dev *d) { }
 +static inline void pci_aer_clear_fatal_status(struct pci_dev *dev) { }
 +static inline void pci_aer_clear_device_status(struct pci_dev *dev) { }
 +#endif
 +
  #endif /* DRIVERS_PCI_H */
diff --combined drivers/pci/quirks.c
@@@ -460,7 -460,6 +460,7 @@@ static void quirk_nfp6000(struct pci_de
  }
  DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME,     PCI_DEVICE_ID_NETRONOME_NFP4000,        quirk_nfp6000);
  DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME,     PCI_DEVICE_ID_NETRONOME_NFP6000,        quirk_nfp6000);
 +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME,     PCI_DEVICE_ID_NETRONOME_NFP5000,        quirk_nfp6000);
  DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME,     PCI_DEVICE_ID_NETRONOME_NFP6000_VF,     quirk_nfp6000);
  
  /*  On IBM Crocodile ipr SAS adapters, expand BAR to system page size */
@@@ -2106,7 -2105,6 +2106,7 @@@ static void quirk_netmos(struct pci_de
                if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM &&
                                dev->subsystem_device == 0x0299)
                        return;
 +              /* else: fall through */
        case PCI_DEVICE_ID_NETMOS_9735:
        case PCI_DEVICE_ID_NETMOS_9745:
        case PCI_DEVICE_ID_NETMOS_9845:
@@@ -4555,27 -4553,79 +4555,79 @@@ static int pci_quirk_enable_intel_spt_p
        return 0;
  }
  
- static const struct pci_dev_enable_acs {
+ static int pci_quirk_disable_intel_spt_pch_acs_redir(struct pci_dev *dev)
+ {
+       int pos;
+       u32 cap, ctrl;
+       if (!pci_quirk_intel_spt_pch_acs_match(dev))
+               return -ENOTTY;
+       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
+       if (!pos)
+               return -ENOTTY;
+       pci_read_config_dword(dev, pos + PCI_ACS_CAP, &cap);
+       pci_read_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, &ctrl);
+       ctrl &= ~(PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC);
+       pci_write_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, ctrl);
+       pci_info(dev, "Intel SPT PCH root port workaround: disabled ACS redirect\n");
+       return 0;
+ }
+ static const struct pci_dev_acs_ops {
        u16 vendor;
        u16 device;
        int (*enable_acs)(struct pci_dev *dev);
- } pci_dev_enable_acs[] = {
-       { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_enable_intel_pch_acs },
-       { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_enable_intel_spt_pch_acs },
-       { 0 }
+       int (*disable_acs_redir)(struct pci_dev *dev);
+ } pci_dev_acs_ops[] = {
+       { PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+           .enable_acs = pci_quirk_enable_intel_pch_acs,
+       },
+       { PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+           .enable_acs = pci_quirk_enable_intel_spt_pch_acs,
+           .disable_acs_redir = pci_quirk_disable_intel_spt_pch_acs_redir,
+       },
  };
  
  int pci_dev_specific_enable_acs(struct pci_dev *dev)
  {
-       const struct pci_dev_enable_acs *i;
-       int ret;
+       const struct pci_dev_acs_ops *p;
+       int i, ret;
+       for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) {
+               p = &pci_dev_acs_ops[i];
+               if ((p->vendor == dev->vendor ||
+                    p->vendor == (u16)PCI_ANY_ID) &&
+                   (p->device == dev->device ||
+                    p->device == (u16)PCI_ANY_ID) &&
+                   p->enable_acs) {
+                       ret = p->enable_acs(dev);
+                       if (ret >= 0)
+                               return ret;
+               }
+       }
  
-       for (i = pci_dev_enable_acs; i->enable_acs; i++) {
-               if ((i->vendor == dev->vendor ||
-                    i->vendor == (u16)PCI_ANY_ID) &&
-                   (i->device == dev->device ||
-                    i->device == (u16)PCI_ANY_ID)) {
-                       ret = i->enable_acs(dev);
+       return -ENOTTY;
+ }
+ int pci_dev_specific_disable_acs_redir(struct pci_dev *dev)
+ {
+       const struct pci_dev_acs_ops *p;
+       int i, ret;
+       for (i = 0; i < ARRAY_SIZE(pci_dev_acs_ops); i++) {
+               p = &pci_dev_acs_ops[i];
+               if ((p->vendor == dev->vendor ||
+                    p->vendor == (u16)PCI_ANY_ID) &&
+                   (p->device == dev->device ||
+                    p->device == (u16)PCI_ANY_ID) &&
+                   p->disable_acs_redir) {
+                       ret = p->disable_acs_redir(dev);
                        if (ret >= 0)
                                return ret;
                }
@@@ -4755,58 -4805,3 +4807,58 @@@ DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDO
                              PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda);
  DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
                              PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda);
 +
 +/*
 + * Some IDT switches incorrectly flag an ACS Source Validation error on
 + * completions for config read requests even though PCIe r4.0, sec
 + * 6.12.1.1, says that completions are never affected by ACS Source
 + * Validation.  Here's the text of IDT 89H32H8G3-YC, erratum #36:
 + *
 + *   Item #36 - Downstream port applies ACS Source Validation to Completions
 + *   Section 6.12.1.1 of the PCI Express Base Specification 3.1 states that
 + *   completions are never affected by ACS Source Validation.  However,
 + *   completions received by a downstream port of the PCIe switch from a
 + *   device that has not yet captured a PCIe bus number are incorrectly
 + *   dropped by ACS Source Validation by the switch downstream port.
 + *
 + * The workaround suggested by IDT is to issue a config write to the
 + * downstream device before issuing the first config read.  This allows the
 + * downstream device to capture its bus and device numbers (see PCIe r4.0,
 + * sec 2.2.9), thus avoiding the ACS error on the completion.
 + *
 + * However, we don't know when the device is ready to accept the config
 + * write, so we do config reads until we receive a non-Config Request Retry
 + * Status, then do the config write.
 + *
 + * To avoid hitting the erratum when doing the config reads, we disable ACS
 + * SV around this process.
 + */
 +int pci_idt_bus_quirk(struct pci_bus *bus, int devfn, u32 *l, int timeout)
 +{
 +      int pos;
 +      u16 ctrl = 0;
 +      bool found;
 +      struct pci_dev *bridge = bus->self;
 +
 +      pos = pci_find_ext_capability(bridge, PCI_EXT_CAP_ID_ACS);
 +
 +      /* Disable ACS SV before initial config reads */
 +      if (pos) {
 +              pci_read_config_word(bridge, pos + PCI_ACS_CTRL, &ctrl);
 +              if (ctrl & PCI_ACS_SV)
 +                      pci_write_config_word(bridge, pos + PCI_ACS_CTRL,
 +                                            ctrl & ~PCI_ACS_SV);
 +      }
 +
 +      found = pci_bus_generic_read_dev_vendor_id(bus, devfn, l, timeout);
 +
 +      /* Write Vendor ID (read-only) so the endpoint latches its bus/dev */
 +      if (found)
 +              pci_bus_write_config_word(bus, devfn, PCI_VENDOR_ID, 0);
 +
 +      /* Re-enable ACS_SV if it was previously enabled */
 +      if (ctrl & PCI_ACS_SV)
 +              pci_write_config_word(bridge, pos + PCI_ACS_CTRL, ctrl);
 +
 +      return found;
 +}
diff --combined include/linux/pci.h
@@@ -299,7 -299,6 +299,7 @@@ struct pci_dev 
        u8              hdr_type;       /* PCI header type (`multi' flag masked out) */
  #ifdef CONFIG_PCIEAER
        u16             aer_cap;        /* AER capability offset */
 +      struct aer_stats *aer_stats;    /* AER stats for this device */
  #endif
        u8              pcie_cap;       /* PCIe capability offset */
        u8              msi_cap;        /* MSI capability offset */
        unsigned int    transparent:1;          /* Subtractive decode bridge */
        unsigned int    multifunction:1;        /* Multi-function device */
  
 -      unsigned int    is_added:1;
        unsigned int    is_busmaster:1;         /* Is busmaster */
        unsigned int    no_msi:1;               /* May not use MSI */
        unsigned int    no_64bit_msi:1;         /* May only use 32-bit MSIs */
        unsigned int    is_virtfn:1;
        unsigned int    reset_fn:1;
        unsigned int    is_hotplug_bridge:1;
 +      unsigned int    shpc_managed:1;         /* SHPC owned by shpchp */
        unsigned int    is_thunderbolt:1;       /* Thunderbolt controller */
        unsigned int    __aer_firmware_first_valid:1;
        unsigned int    __aer_firmware_first:1;
@@@ -820,21 -819,6 +820,21 @@@ struct pci_driver 
        .vendor = PCI_VENDOR_ID_##vend, .device = (dev), \
        .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0
  
 +/**
 + * PCI_DEVICE_DATA - macro used to describe a specific PCI device in very short form
 + * @vend: the vendor name (without PCI_VENDOR_ID_ prefix)
 + * @dev: the device name (without PCI_DEVICE_ID_<vend>_ prefix)
 + * @data: the driver data to be filled
 + *
 + * This macro is used to create a struct pci_device_id that matches a
 + * specific PCI device.  The subvendor, and subdevice fields will be set
 + * to PCI_ANY_ID.
 + */
 +#define PCI_DEVICE_DATA(vend, dev, data) \
 +      .vendor = PCI_VENDOR_ID_##vend, .device = PCI_DEVICE_ID_##vend##_##dev, \
 +      .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0, \
 +      .driver_data = (kernel_ulong_t)(data)
 +
  enum {
        PCI_REASSIGN_ALL_RSRC   = 0x00000001,   /* Ignore firmware setup */
        PCI_REASSIGN_ALL_BUS    = 0x00000002,   /* Reassign all bus numbers */
@@@ -1256,8 -1240,6 +1256,8 @@@ int pci_register_io_range(struct fwnode
  unsigned long pci_address_to_pio(phys_addr_t addr);
  phys_addr_t pci_pio_to_address(unsigned long pio);
  int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr);
 +int devm_pci_remap_iospace(struct device *dev, const struct resource *res,
 +                         phys_addr_t phys_addr);
  void pci_unmap_iospace(struct resource *res);
  void __iomem *devm_pci_remap_cfgspace(struct device *dev,
                                      resource_size_t offset,
@@@ -1486,9 -1468,13 +1486,9 @@@ static inline bool pcie_aspm_support_en
  #endif
  
  #ifdef CONFIG_PCIEAER
 -void pci_no_aer(void);
  bool pci_aer_available(void);
 -int pci_aer_init(struct pci_dev *dev);
  #else
 -static inline void pci_no_aer(void) { }
  static inline bool pci_aer_available(void) { return false; }
 -static inline int pci_aer_init(struct pci_dev *d) { return -ENODEV; }
  #endif
  
  #ifdef CONFIG_PCIE_ECRC
@@@ -1890,20 -1876,9 +1890,9 @@@ enum pci_fixup_pass 
  
  #ifdef CONFIG_PCI_QUIRKS
  void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev);
- int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
- int 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 int pci_dev_specific_acs_enabled(struct pci_dev *dev,
-                                              u16 acs_flags)
- {
-       return -ENOTTY;
- }
- static inline int pci_dev_specific_enable_acs(struct pci_dev *dev)
- {
-       return -ENOTTY;
- }
  #endif
  
  void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen);