Merge branch 'pci/enumeration'

- 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
This commit is contained in:
Bjorn Helgaas 2022-12-10 10:36:32 -06:00
commit 51ef4873c6
7 changed files with 88 additions and 49 deletions

View File

@ -350,6 +350,11 @@ bool pcie_cap_has_lnkctl(const struct pci_dev *dev)
type == PCI_EXP_TYPE_PCIE_BRIDGE; 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) static inline bool pcie_cap_has_sltctl(const struct pci_dev *dev)
{ {
return pcie_downstream_port(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); return pcie_cap_has_rtctl(dev);
case PCI_EXP_DEVCAP2: case PCI_EXP_DEVCAP2:
case PCI_EXP_DEVCTL2: case PCI_EXP_DEVCTL2:
return pcie_cap_version(dev) > 1;
case PCI_EXP_LNKCAP2: case PCI_EXP_LNKCAP2:
case PCI_EXP_LNKCTL2: case PCI_EXP_LNKCTL2:
case PCI_EXP_LNKSTA2: case PCI_EXP_LNKSTA2:
return pcie_cap_version(dev) > 1; return pcie_cap_has_lnkctl2(dev);
default: default:
return false; return false;
} }

View File

@ -44,6 +44,8 @@ int pci_request_irq(struct pci_dev *dev, unsigned int nr, irq_handler_t handler,
va_start(ap, fmt); va_start(ap, fmt);
devname = kvasprintf(GFP_KERNEL, fmt, ap); devname = kvasprintf(GFP_KERNEL, fmt, ap);
va_end(ap); va_end(ap);
if (!devname)
return -ENOMEM;
ret = request_threaded_irq(pci_irq_vector(dev, nr), handler, thread_fn, ret = request_threaded_irq(pci_irq_vector(dev, nr), handler, thread_fn,
irqflags, devname, dev_id); irqflags, devname, dev_id);

View File

@ -6447,6 +6447,8 @@ bool pci_device_is_present(struct pci_dev *pdev)
{ {
u32 v; 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)) if (pci_dev_is_disconnected(pdev))
return false; return false;
return pci_bus_read_dev_vendor_id(pdev->bus, pdev->devfn, &v, 0); 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 #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 of_pci_bus_find_domain_nr(struct device *parent)
{ {
static int use_dt_domains = -1; static bool static_domains_reserved = false;
int domain = -1; int domain_nr;
if (parent) /* On the first call scan device tree for static allocations. */
domain = of_get_pci_domain_nr(parent->of_node); if (!static_domains_reserved) {
of_pci_reserve_static_domain_nr();
/* static_domains_reserved = true;
* 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 >= 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 domain; 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);
}
/*
* 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.
*/
return ida_alloc(&pci_domain_nr_dynamic_ida, GFP_KERNEL);
}
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) 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) : return acpi_disabled ? of_pci_bus_find_domain_nr(parent) :
acpi_pci_bus_find_domain_nr(bus); 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 #endif
/** /**

View File

@ -15,6 +15,7 @@ extern const unsigned char pcie_link_speed[];
extern bool pci_early_dump; extern bool pci_early_dump;
bool pcie_cap_has_lnkctl(const struct pci_dev *dev); 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); bool pcie_cap_has_rtctl(const struct pci_dev *dev);
/* Functions internal to the PCI core code */ /* Functions internal to the PCI core code */

View 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); bus->domain_nr = pci_bus_find_domain_nr(bus, parent);
else else
bus->domain_nr = bridge->domain_nr; bus->domain_nr = bridge->domain_nr;
if (bus->domain_nr < 0) {
err = bus->domain_nr;
goto free;
}
#endif #endif
b = pci_find_bus(pci_domain_nr(bus), bridge->busnr); b = pci_find_bus(pci_domain_nr(bus), bridge->busnr);
@ -1030,6 +1034,9 @@ unregister:
device_del(&bridge->dev); device_del(&bridge->dev);
free: free:
#ifdef CONFIG_PCI_DOMAINS_GENERIC
pci_bus_release_domain_nr(bus, parent);
#endif
kfree(bus); kfree(bus);
return err; return err;
} }
@ -1891,9 +1898,6 @@ int pci_setup_device(struct pci_dev *dev)
dev->broken_intx_masking = pci_intx_mask_broken(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 */ switch (dev->hdr_type) { /* header type */
case PCI_HEADER_TYPE_NORMAL: /* standard header */ case PCI_HEADER_TYPE_NORMAL: /* standard header */
if (class == PCI_CLASS_BRIDGE_PCI) if (class == PCI_CLASS_BRIDGE_PCI)

View File

@ -160,6 +160,12 @@ void pci_remove_root_bus(struct pci_bus *bus)
pci_remove_bus(bus); pci_remove_bus(bus);
host_bridge->bus = NULL; 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 */ /* remove the host bridge */
device_del(&host_bridge->dev); device_del(&host_bridge->dev);
} }

View File

@ -1726,6 +1726,7 @@ static inline int acpi_pci_bus_find_domain_nr(struct pci_bus *bus)
{ return 0; } { return 0; }
#endif #endif
int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent); 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 #endif
/* Some architectures require additional setup to direct VGA traffic */ /* Some architectures require additional setup to direct VGA traffic */