summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/pci/pci.c103
-rw-r--r--drivers/pci/probe.c7
-rw-r--r--drivers/pci/remove.c6
-rw-r--r--include/linux/pci.h1
4 files changed, 74 insertions, 43 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 2127aba3550b..9f3cc829dfee 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -6743,60 +6743,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 +6814,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
/**
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 1d6f7b502020..1e234189aff1 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -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;
}
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 4c54c75050dc..0145aef1b930 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -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);
}
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 2bda4a4e47e8..28af4414f789 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -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 */