aboutsummaryrefslogtreecommitdiff
path: root/nixos/modules/omnia-kernel-patches/0016-PCI-Assign-PCI-domain-by-ida_alloc.patch
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/omnia-kernel-patches/0016-PCI-Assign-PCI-domain-by-ida_alloc.patch')
-rw-r--r--nixos/modules/omnia-kernel-patches/0016-PCI-Assign-PCI-domain-by-ida_alloc.patch215
1 files changed, 215 insertions, 0 deletions
diff --git a/nixos/modules/omnia-kernel-patches/0016-PCI-Assign-PCI-domain-by-ida_alloc.patch b/nixos/modules/omnia-kernel-patches/0016-PCI-Assign-PCI-domain-by-ida_alloc.patch
new file mode 100644
index 0000000..a349fb1
--- /dev/null
+++ b/nixos/modules/omnia-kernel-patches/0016-PCI-Assign-PCI-domain-by-ida_alloc.patch
@@ -0,0 +1,215 @@
+From 07414acbdce72901154b39226c8707b5a92565b7 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Sat, 2 Jul 2022 21:37:51 +0200
+Subject: [PATCH 16/53] PCI: Assign PCI domain by ida_alloc()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Replace assignment of PCI domain from atomic_inc_return() to ida_alloc().
+
+Use two IDAs, one for static domain allocations (those which are defined in
+device tree) and second for dynamic allocations (all other).
+
+During removal of root bus / host bridge release also allocated domain id.
+So released id can be reused again, for example in situation when
+dynamically loading and unloading native PCI host bridge drivers.
+
+This change also allows to mix static device tree assignment and dynamic by
+kernel as all static allocations are reserved in dynamic pool.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+---
+ drivers/pci/pci.c | 103 +++++++++++++++++++++++++------------------
+ drivers/pci/probe.c | 5 +++
+ drivers/pci/remove.c | 6 +++
+ include/linux/pci.h | 1 +
+ 4 files changed, 72 insertions(+), 43 deletions(-)
+
+diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
+index 95bc329e74c0..4589ad52e4ca 100644
+--- a/drivers/pci/pci.c
++++ b/drivers/pci/pci.c
+@@ -6758,60 +6758,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 then allocate it in static IDA.
++ * This prevent 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 then choose 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 in which was it 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)
+@@ -6819,6 +6829,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 c5286b027f00..a8db8bf0f196 100644
+--- a/drivers/pci/probe.c
++++ b/drivers/pci/probe.c
+@@ -906,6 +906,8 @@ 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)
++ goto free;
+ #endif
+
+ b = pci_find_bus(pci_domain_nr(bus), bridge->busnr);
+@@ -1030,6 +1032,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;
+ }
+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 060af91bafcd..c7abe91899d2 100644
+--- a/include/linux/pci.h
++++ b/include/linux/pci.h
+@@ -1723,6 +1723,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 */
+--
+2.37.3
+