Commit 51ef4873 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

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
parents cad4f43f 2d9cd957
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -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;
	}
+2 −0
Original line number Diff line number Diff line
@@ -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);
+62 −43
Original line number Diff line number Diff line
@@ -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) {
		/*
	 * 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;
		 * 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);
}

	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

/**
+1 −0
Original line number Diff line number Diff line
@@ -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 */
+7 −3
Original line number Diff line number Diff line
@@ -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 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
	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)
Loading