Skip to content

Commit c14f7cc

Browse files
palibjorn-helgaas
authored andcommitted
PCI: Assign PCI domain IDs by ida_alloc()
Replace assignment of PCI domain IDs 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, also release the domain ID. The released ID can be reused again, for example 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. [bhelgaas: set "err" if "bus->domain_nr < 0"] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Pali Rohár <[email protected]> Signed-off-by: Bjorn Helgaas <[email protected]>
1 parent 44e9859 commit c14f7cc

File tree

4 files changed

+74
-43
lines changed

4 files changed

+74
-43
lines changed

drivers/pci/pci.c

Lines changed: 60 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6743,67 +6743,84 @@ static void pci_no_domains(void)
67436743
}
67446744

67456745
#ifdef CONFIG_PCI_DOMAINS_GENERIC
6746-
static atomic_t __domain_nr = ATOMIC_INIT(-1);
6746+
static DEFINE_IDA(pci_domain_nr_static_ida);
6747+
static DEFINE_IDA(pci_domain_nr_dynamic_ida);
67476748

6748-
static int pci_get_new_domain_nr(void)
6749+
static void of_pci_reserve_static_domain_nr(void)
67496750
{
6750-
return atomic_inc_return(&__domain_nr);
6751+
struct device_node *np;
6752+
int domain_nr;
6753+
6754+
for_each_node_by_type(np, "pci") {
6755+
domain_nr = of_get_pci_domain_nr(np);
6756+
if (domain_nr < 0)
6757+
continue;
6758+
/*
6759+
* Permanently allocate domain_nr in dynamic_ida
6760+
* to prevent it from dynamic allocation.
6761+
*/
6762+
ida_alloc_range(&pci_domain_nr_dynamic_ida,
6763+
domain_nr, domain_nr, GFP_KERNEL);
6764+
}
67516765
}
67526766

67536767
static int of_pci_bus_find_domain_nr(struct device *parent)
67546768
{
6755-
static int use_dt_domains = -1;
6756-
int domain = -1;
6769+
static bool static_domains_reserved = false;
6770+
int domain_nr;
67576771

6758-
if (parent)
6759-
domain = of_get_pci_domain_nr(parent->of_node);
6772+
/* On the first call scan device tree for static allocations. */
6773+
if (!static_domains_reserved) {
6774+
of_pci_reserve_static_domain_nr();
6775+
static_domains_reserved = true;
6776+
}
6777+
6778+
if (parent) {
6779+
/*
6780+
* If domain is in DT, allocate it in static IDA. This
6781+
* prevents duplicate static allocations in case of errors
6782+
* in DT.
6783+
*/
6784+
domain_nr = of_get_pci_domain_nr(parent->of_node);
6785+
if (domain_nr >= 0)
6786+
return ida_alloc_range(&pci_domain_nr_static_ida,
6787+
domain_nr, domain_nr,
6788+
GFP_KERNEL);
6789+
}
67606790

67616791
/*
6762-
* Check DT domain and use_dt_domains values.
6763-
*
6764-
* If DT domain property is valid (domain >= 0) and
6765-
* use_dt_domains != 0, the DT assignment is valid since this means
6766-
* we have not previously allocated a domain number by using
6767-
* pci_get_new_domain_nr(); we should also update use_dt_domains to
6768-
* 1, to indicate that we have just assigned a domain number from
6769-
* DT.
6770-
*
6771-
* If DT domain property value is not valid (ie domain < 0), and we
6772-
* have not previously assigned a domain number from DT
6773-
* (use_dt_domains != 1) we should assign a domain number by
6774-
* using the:
6775-
*
6776-
* pci_get_new_domain_nr()
6777-
*
6778-
* API and update the use_dt_domains value to keep track of method we
6779-
* are using to assign domain numbers (use_dt_domains = 0).
6780-
*
6781-
* All other combinations imply we have a platform that is trying
6782-
* to mix domain numbers obtained from DT and pci_get_new_domain_nr(),
6783-
* which is a recipe for domain mishandling and it is prevented by
6784-
* invalidating the domain value (domain = -1) and printing a
6785-
* corresponding error.
6792+
* If domain was not specified in DT, choose a free ID from dynamic
6793+
* allocations. All domain numbers from DT are permanently in
6794+
* dynamic allocations to prevent assigning them to other DT nodes
6795+
* without static domain.
67866796
*/
6787-
if (domain >= 0 && use_dt_domains) {
6788-
use_dt_domains = 1;
6789-
} else if (domain < 0 && use_dt_domains != 1) {
6790-
use_dt_domains = 0;
6791-
domain = pci_get_new_domain_nr();
6792-
} else {
6793-
if (parent)
6794-
pr_err("Node %pOF has ", parent->of_node);
6795-
pr_err("Inconsistent \"linux,pci-domain\" property in DT\n");
6796-
domain = -1;
6797-
}
6797+
return ida_alloc(&pci_domain_nr_dynamic_ida, GFP_KERNEL);
6798+
}
67986799

6799-
return domain;
6800+
static void of_pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent)
6801+
{
6802+
if (bus->domain_nr < 0)
6803+
return;
6804+
6805+
/* Release domain from IDA where it was allocated. */
6806+
if (of_get_pci_domain_nr(parent->of_node) == bus->domain_nr)
6807+
ida_free(&pci_domain_nr_static_ida, bus->domain_nr);
6808+
else
6809+
ida_free(&pci_domain_nr_dynamic_ida, bus->domain_nr);
68006810
}
68016811

68026812
int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent)
68036813
{
68046814
return acpi_disabled ? of_pci_bus_find_domain_nr(parent) :
68056815
acpi_pci_bus_find_domain_nr(bus);
68066816
}
6817+
6818+
void pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent)
6819+
{
6820+
if (!acpi_disabled)
6821+
return;
6822+
of_pci_bus_release_domain_nr(bus, parent);
6823+
}
68076824
#endif
68086825

68096826
/**

drivers/pci/probe.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,10 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
906906
bus->domain_nr = pci_bus_find_domain_nr(bus, parent);
907907
else
908908
bus->domain_nr = bridge->domain_nr;
909+
if (bus->domain_nr < 0) {
910+
err = bus->domain_nr;
911+
goto free;
912+
}
909913
#endif
910914

911915
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)
10301034
device_del(&bridge->dev);
10311035

10321036
free:
1037+
#ifdef CONFIG_PCI_DOMAINS_GENERIC
1038+
pci_bus_release_domain_nr(bus, parent);
1039+
#endif
10331040
kfree(bus);
10341041
return err;
10351042
}

drivers/pci/remove.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,12 @@ void pci_remove_root_bus(struct pci_bus *bus)
160160
pci_remove_bus(bus);
161161
host_bridge->bus = NULL;
162162

163+
#ifdef CONFIG_PCI_DOMAINS_GENERIC
164+
/* Release domain_nr if it was dynamically allocated */
165+
if (host_bridge->domain_nr == PCI_DOMAIN_NR_NOT_SET)
166+
pci_bus_release_domain_nr(bus, host_bridge->dev.parent);
167+
#endif
168+
163169
/* remove the host bridge */
164170
device_del(&host_bridge->dev);
165171
}

include/linux/pci.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,6 +1726,7 @@ static inline int acpi_pci_bus_find_domain_nr(struct pci_bus *bus)
17261726
{ return 0; }
17271727
#endif
17281728
int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent);
1729+
void pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent);
17291730
#endif
17301731

17311732
/* Some architectures require additional setup to direct VGA traffic */

0 commit comments

Comments
 (0)