OSDN Git Service

Merge branch 'pci/enumeration'
authorBjorn Helgaas <bhelgaas@google.com>
Sat, 10 Dec 2022 16:36:32 +0000 (10:36 -0600)
committerBjorn Helgaas <bhelgaas@google.com>
Sat, 10 Dec 2022 16:36:32 +0000 (10:36 -0600)
- Only read/write PCIe Link 2 registers for devices with Links and PCIe
  Capability version >= 2 (Maciej W. Rozycki)

- Revert a patch that cleared PCI_STATUS during enumeration because it
  broke Linux guests on Apple's virtualization framework (Bjorn Helgaas)

- Assign PCI domain IDs using IDAs so IDs can be easily reused after
  loading/unloading host bridge drivers (Pali Rohár)

- Fix pci_device_is_present(), which previously always returned "false" for
  VFs because their vendor ID is always 0xfff (Michael S. Tsirkin)

- Check for alloc failure in pci_request_irq() (Zeng Heng)

* pci/enumeration:
  PCI: Check for alloc failure in pci_request_irq()
  PCI: Fix pci_device_is_present() for VFs by checking PF
  PCI: Assign PCI domain IDs by ida_alloc()
  Revert "PCI: Clear PCI_STATUS when setting up device"
  PCI: Access Link 2 registers only for devices with Links

drivers/pci/access.c
drivers/pci/irq.c
drivers/pci/pci.c
drivers/pci/pci.h
drivers/pci/probe.c
drivers/pci/remove.c
include/linux/pci.h

index 708c752..3c230ca 100644 (file)
@@ -350,6 +350,11 @@ bool pcie_cap_has_lnkctl(const struct pci_dev *dev)
               type == PCI_EXP_TYPE_PCIE_BRIDGE;
 }
 
+bool pcie_cap_has_lnkctl2(const struct pci_dev *dev)
+{
+       return pcie_cap_has_lnkctl(dev) && pcie_cap_version(dev) > 1;
+}
+
 static inline bool pcie_cap_has_sltctl(const struct pci_dev *dev)
 {
        return pcie_downstream_port(dev) &&
@@ -390,10 +395,11 @@ static bool pcie_capability_reg_implemented(struct pci_dev *dev, int pos)
                return pcie_cap_has_rtctl(dev);
        case PCI_EXP_DEVCAP2:
        case PCI_EXP_DEVCTL2:
+               return pcie_cap_version(dev) > 1;
        case PCI_EXP_LNKCAP2:
        case PCI_EXP_LNKCTL2:
        case PCI_EXP_LNKSTA2:
-               return pcie_cap_version(dev) > 1;
+               return pcie_cap_has_lnkctl2(dev);
        default:
                return false;
        }
index 12ecd0a..0050e8f 100644 (file)
@@ -44,6 +44,8 @@ int pci_request_irq(struct pci_dev *dev, unsigned int nr, irq_handler_t handler,
        va_start(ap, fmt);
        devname = kvasprintf(GFP_KERNEL, fmt, ap);
        va_end(ap);
+       if (!devname)
+               return -ENOMEM;
 
        ret = request_threaded_irq(pci_irq_vector(dev, nr), handler, thread_fn,
                                   irqflags, devname, dev_id);
index 2127aba..fba9548 100644 (file)
@@ -6447,6 +6447,8 @@ bool pci_device_is_present(struct pci_dev *pdev)
 {
        u32 v;
 
+       /* Check PF if pdev is a VF, since VF Vendor/Device IDs are 0xffff */
+       pdev = pci_physfn(pdev);
        if (pci_dev_is_disconnected(pdev))
                return false;
        return pci_bus_read_dev_vendor_id(pdev->bus, pdev->devfn, &v, 0);
@@ -6743,60 +6745,70 @@ static void pci_no_domains(void)
 }
 
 #ifdef CONFIG_PCI_DOMAINS_GENERIC
-static atomic_t __domain_nr = ATOMIC_INIT(-1);
+static DEFINE_IDA(pci_domain_nr_static_ida);
+static DEFINE_IDA(pci_domain_nr_dynamic_ida);
 
-static int pci_get_new_domain_nr(void)
+static void of_pci_reserve_static_domain_nr(void)
 {
-       return atomic_inc_return(&__domain_nr);
+       struct device_node *np;
+       int domain_nr;
+
+       for_each_node_by_type(np, "pci") {
+               domain_nr = of_get_pci_domain_nr(np);
+               if (domain_nr < 0)
+                       continue;
+               /*
+                * Permanently allocate domain_nr in dynamic_ida
+                * to prevent it from dynamic allocation.
+                */
+               ida_alloc_range(&pci_domain_nr_dynamic_ida,
+                               domain_nr, domain_nr, GFP_KERNEL);
+       }
 }
 
 static int of_pci_bus_find_domain_nr(struct device *parent)
 {
-       static int use_dt_domains = -1;
-       int domain = -1;
+       static bool static_domains_reserved = false;
+       int domain_nr;
 
-       if (parent)
-               domain = of_get_pci_domain_nr(parent->of_node);
+       /* On the first call scan device tree for static allocations. */
+       if (!static_domains_reserved) {
+               of_pci_reserve_static_domain_nr();
+               static_domains_reserved = true;
+       }
+
+       if (parent) {
+               /*
+                * If domain is in DT, allocate it in static IDA.  This
+                * prevents duplicate static allocations in case of errors
+                * in DT.
+                */
+               domain_nr = of_get_pci_domain_nr(parent->of_node);
+               if (domain_nr >= 0)
+                       return ida_alloc_range(&pci_domain_nr_static_ida,
+                                              domain_nr, domain_nr,
+                                              GFP_KERNEL);
+       }
 
        /*
-        * Check DT domain and use_dt_domains values.
-        *
-        * If DT domain property is valid (domain >= 0) and
-        * use_dt_domains != 0, the DT assignment is valid since this means
-        * we have not previously allocated a domain number by using
-        * pci_get_new_domain_nr(); we should also update use_dt_domains to
-        * 1, to indicate that we have just assigned a domain number from
-        * DT.
-        *
-        * If DT domain property value is not valid (ie domain < 0), and we
-        * have not previously assigned a domain number from DT
-        * (use_dt_domains != 1) we should assign a domain number by
-        * using the:
-        *
-        * pci_get_new_domain_nr()
-        *
-        * API and update the use_dt_domains value to keep track of method we
-        * are using to assign domain numbers (use_dt_domains = 0).
-        *
-        * All other combinations imply we have a platform that is trying
-        * to mix domain numbers obtained from DT and pci_get_new_domain_nr(),
-        * which is a recipe for domain mishandling and it is prevented by
-        * invalidating the domain value (domain = -1) and printing a
-        * corresponding error.
+        * If domain was not specified in DT, choose a free ID from dynamic
+        * allocations. All domain numbers from DT are permanently in
+        * dynamic allocations to prevent assigning them to other DT nodes
+        * without static domain.
         */
-       if (domain >= 0 && use_dt_domains) {
-               use_dt_domains = 1;
-       } else if (domain < 0 && use_dt_domains != 1) {
-               use_dt_domains = 0;
-               domain = pci_get_new_domain_nr();
-       } else {
-               if (parent)
-                       pr_err("Node %pOF has ", parent->of_node);
-               pr_err("Inconsistent \"linux,pci-domain\" property in DT\n");
-               domain = -1;
-       }
+       return ida_alloc(&pci_domain_nr_dynamic_ida, GFP_KERNEL);
+}
 
-       return domain;
+static void of_pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent)
+{
+       if (bus->domain_nr < 0)
+               return;
+
+       /* Release domain from IDA where it was allocated. */
+       if (of_get_pci_domain_nr(parent->of_node) == bus->domain_nr)
+               ida_free(&pci_domain_nr_static_ida, bus->domain_nr);
+       else
+               ida_free(&pci_domain_nr_dynamic_ida, bus->domain_nr);
 }
 
 int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent)
@@ -6804,6 +6816,13 @@ int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent)
        return acpi_disabled ? of_pci_bus_find_domain_nr(parent) :
                               acpi_pci_bus_find_domain_nr(bus);
 }
+
+void pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent)
+{
+       if (!acpi_disabled)
+               return;
+       of_pci_bus_release_domain_nr(bus, parent);
+}
 #endif
 
 /**
index b1ebb7a..9ed3b55 100644 (file)
@@ -15,6 +15,7 @@ extern const unsigned char pcie_link_speed[];
 extern bool pci_early_dump;
 
 bool pcie_cap_has_lnkctl(const struct pci_dev *dev);
+bool pcie_cap_has_lnkctl2(const struct pci_dev *dev);
 bool pcie_cap_has_rtctl(const struct pci_dev *dev);
 
 /* Functions internal to the PCI core code */
index b66fa42..1e23418 100644 (file)
@@ -906,6 +906,10 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
                bus->domain_nr = pci_bus_find_domain_nr(bus, parent);
        else
                bus->domain_nr = bridge->domain_nr;
+       if (bus->domain_nr < 0) {
+               err = bus->domain_nr;
+               goto free;
+       }
 #endif
 
        b = pci_find_bus(pci_domain_nr(bus), bridge->busnr);
@@ -1030,6 +1034,9 @@ unregister:
        device_del(&bridge->dev);
 
 free:
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
+       pci_bus_release_domain_nr(bus, parent);
+#endif
        kfree(bus);
        return err;
 }
@@ -1891,9 +1898,6 @@ int pci_setup_device(struct pci_dev *dev)
 
        dev->broken_intx_masking = pci_intx_mask_broken(dev);
 
-       /* Clear errors left from system firmware */
-       pci_write_config_word(dev, PCI_STATUS, 0xffff);
-
        switch (dev->hdr_type) {                    /* header type */
        case PCI_HEADER_TYPE_NORMAL:                /* standard header */
                if (class == PCI_CLASS_BRIDGE_PCI)
index 4c54c75..0145aef 100644 (file)
@@ -160,6 +160,12 @@ void pci_remove_root_bus(struct pci_bus *bus)
        pci_remove_bus(bus);
        host_bridge->bus = NULL;
 
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
+       /* Release domain_nr if it was dynamically allocated */
+       if (host_bridge->domain_nr == PCI_DOMAIN_NR_NOT_SET)
+               pci_bus_release_domain_nr(bus, host_bridge->dev.parent);
+#endif
+
        /* remove the host bridge */
        device_del(&host_bridge->dev);
 }
index 2bda4a4..28af441 100644 (file)
@@ -1726,6 +1726,7 @@ static inline int acpi_pci_bus_find_domain_nr(struct pci_bus *bus)
 { return 0; }
 #endif
 int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent);
+void pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent);
 #endif
 
 /* Some architectures require additional setup to direct VGA traffic */