]> Gentwo Git Trees - linux/.git/commitdiff
PCI: Enable host bridge emulation for PCI_DOMAINS_GENERIC platforms
authorDan Williams <dan.j.williams@intel.com>
Fri, 24 Oct 2025 22:46:21 +0000 (15:46 -0700)
committerBjorn Helgaas <bhelgaas@google.com>
Tue, 28 Oct 2025 17:36:34 +0000 (12:36 -0500)
The ability to emulate a host bridge is useful not only for hardware PCI
controllers like CONFIG_VMD, or virtual PCI controllers like
CONFIG_PCI_HYPERV, but also for test and development scenarios like
CONFIG_SAMPLES_DEVSEC [1].

One stumbling block for defining CONFIG_SAMPLES_DEVSEC, a sample
implementation of a platform TSM for PCI Device Security, is the need to
accommodate PCI_DOMAINS_GENERIC architectures alongside x86 [2].

In support of supplementing the existing CONFIG_PCI_BRIDGE_EMUL
infrastructure for host bridges:

* Introduce pci_bus_find_emul_domain_nr() as a common way to find a free
  PCI domain number whether that is to reuse the existing dynamic
  allocation code in the !ACPI case, or to assign an unused domain above
  the last ACPI segment.

* Convert pci-hyperv to the new allocator so that the PCI core can
  unconditionally assume that bridge->domain_nr != PCI_DOMAIN_NR_NOT_SET
  is the dynamically allocated case.

A follow on patch can also convert vmd to the new scheme. Currently vmd is
limited to CONFIG_PCI_DOMAINS_GENERIC=n (x86) so, unlike pci-hyperv, it
does not immediately conflict with this new pci_bus_find_emul_domain_nr()
mechanism.

Link: http://lore.kernel.org/174107249038.1288555.12362100502109498455.stgit@dwillia2-xfh.jf.intel.com
Reported-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Closes: http://lore.kernel.org/20250311144601.145736-3-suzuki.poulose@arm.com [2]
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Tested-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Tested-by: Michael Kelley <mhklinux@outlook.com>
Reviewed-by: Michael Kelley <mhklinux@outlook.com>
Cc: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Cc: Rob Herring <robh@kernel.org>
Cc: K. Y. Srinivasan <kys@microsoft.com>
Cc: Haiyang Zhang <haiyangz@microsoft.com>
Cc: Wei Liu <wei.liu@kernel.org>
Cc: Dexuan Cui <decui@microsoft.com>
Link: https://patch.msgid.link/20251024224622.1470555-2-dan.j.williams@intel.com
drivers/pci/controller/pci-hyperv.c
drivers/pci/pci.c
drivers/pci/probe.c
include/linux/pci.h

index 146b43981b27851f8a54839c36b32b9662d6e494..1e237d3538f9c40fc697db6370e7354c88a41db1 100644 (file)
@@ -3696,48 +3696,6 @@ static int hv_send_resources_released(struct hv_device *hdev)
        return 0;
 }
 
-#define HVPCI_DOM_MAP_SIZE (64 * 1024)
-static DECLARE_BITMAP(hvpci_dom_map, HVPCI_DOM_MAP_SIZE);
-
-/*
- * PCI domain number 0 is used by emulated devices on Gen1 VMs, so define 0
- * as invalid for passthrough PCI devices of this driver.
- */
-#define HVPCI_DOM_INVALID 0
-
-/**
- * hv_get_dom_num() - Get a valid PCI domain number
- * Check if the PCI domain number is in use, and return another number if
- * it is in use.
- *
- * @dom: Requested domain number
- *
- * return: domain number on success, HVPCI_DOM_INVALID on failure
- */
-static u16 hv_get_dom_num(u16 dom)
-{
-       unsigned int i;
-
-       if (test_and_set_bit(dom, hvpci_dom_map) == 0)
-               return dom;
-
-       for_each_clear_bit(i, hvpci_dom_map, HVPCI_DOM_MAP_SIZE) {
-               if (test_and_set_bit(i, hvpci_dom_map) == 0)
-                       return i;
-       }
-
-       return HVPCI_DOM_INVALID;
-}
-
-/**
- * hv_put_dom_num() - Mark the PCI domain number as free
- * @dom: Domain number to be freed
- */
-static void hv_put_dom_num(u16 dom)
-{
-       clear_bit(dom, hvpci_dom_map);
-}
-
 /**
  * hv_pci_probe() - New VMBus channel probe, for a root PCI bus
  * @hdev:      VMBus's tracking struct for this root PCI bus
@@ -3750,9 +3708,9 @@ static int hv_pci_probe(struct hv_device *hdev,
 {
        struct pci_host_bridge *bridge;
        struct hv_pcibus_device *hbus;
-       u16 dom_req, dom;
+       int ret, dom;
+       u16 dom_req;
        char *name;
-       int ret;
 
        bridge = devm_pci_alloc_host_bridge(&hdev->device, 0);
        if (!bridge)
@@ -3779,11 +3737,14 @@ static int hv_pci_probe(struct hv_device *hdev,
         * PCI bus (which is actually emulated by the hypervisor) is domain 0.
         * (2) There will be no overlap between domains (after fixing possible
         * collisions) in the same VM.
+        *
+        * Because Gen1 VMs use domain 0, don't allow picking domain 0 here,
+        * even if bytes 4 and 5 of the instance GUID are both zero. For wider
+        * userspace compatibility, limit the domain ID to a 16-bit value.
         */
        dom_req = hdev->dev_instance.b[5] << 8 | hdev->dev_instance.b[4];
-       dom = hv_get_dom_num(dom_req);
-
-       if (dom == HVPCI_DOM_INVALID) {
+       dom = pci_bus_find_emul_domain_nr(dom_req, 1, U16_MAX);
+       if (dom < 0) {
                dev_err(&hdev->device,
                        "Unable to use dom# 0x%x or other numbers", dom_req);
                ret = -EINVAL;
@@ -3917,7 +3878,7 @@ static int hv_pci_probe(struct hv_device *hdev,
 destroy_wq:
        destroy_workqueue(hbus->wq);
 free_dom:
-       hv_put_dom_num(hbus->bridge->domain_nr);
+       pci_bus_release_emul_domain_nr(hbus->bridge->domain_nr);
 free_bus:
        kfree(hbus);
        return ret;
@@ -4042,8 +4003,6 @@ static void hv_pci_remove(struct hv_device *hdev)
        irq_domain_remove(hbus->irq_domain);
        irq_domain_free_fwnode(hbus->fwnode);
 
-       hv_put_dom_num(hbus->bridge->domain_nr);
-
        kfree(hbus);
 }
 
@@ -4217,9 +4176,6 @@ static int __init init_hv_pci_drv(void)
        if (ret)
                return ret;
 
-       /* Set the invalid domain number's bit, so it will not be used */
-       set_bit(HVPCI_DOM_INVALID, hvpci_dom_map);
-
        /* Initialize PCI block r/w interface */
        hvpci_block_ops.read_block = hv_read_config_block;
        hvpci_block_ops.write_block = hv_write_config_block;
index b14dd064006cca80ec5275e45a35d6dc2b4d0bbc..f4ccb948e59d4da7cedb1d97e9dd96fe9f0b965d 100644 (file)
@@ -6656,9 +6656,31 @@ static void pci_no_domains(void)
 #endif
 }
 
+#ifdef CONFIG_PCI_DOMAINS
+static DEFINE_IDA(pci_domain_nr_dynamic_ida);
+
+/**
+ * pci_bus_find_emul_domain_nr() - allocate a PCI domain number per constraints
+ * @hint: desired domain, 0 if any ID in the range of @min to @max is acceptable
+ * @min: minimum allowable domain
+ * @max: maximum allowable domain, no IDs higher than INT_MAX will be returned
+ */
+int pci_bus_find_emul_domain_nr(u32 hint, u32 min, u32 max)
+{
+       return ida_alloc_range(&pci_domain_nr_dynamic_ida, max(hint, min), max,
+                              GFP_KERNEL);
+}
+EXPORT_SYMBOL_GPL(pci_bus_find_emul_domain_nr);
+
+void pci_bus_release_emul_domain_nr(int domain_nr)
+{
+       ida_free(&pci_domain_nr_dynamic_ida, domain_nr);
+}
+EXPORT_SYMBOL_GPL(pci_bus_release_emul_domain_nr);
+#endif
+
 #ifdef CONFIG_PCI_DOMAINS_GENERIC
 static DEFINE_IDA(pci_domain_nr_static_ida);
-static DEFINE_IDA(pci_domain_nr_dynamic_ida);
 
 static void of_pci_reserve_static_domain_nr(void)
 {
index c83e75a0ec1263298aeac7f84bcf5513b003496c..c3f6e27144406e1d0a760552d1417f299b865407 100644 (file)
@@ -657,6 +657,11 @@ static void pci_release_host_bridge_dev(struct device *dev)
 
        pci_free_resource_list(&bridge->windows);
        pci_free_resource_list(&bridge->dma_ranges);
+
+       /* Host bridges only have domain_nr set in the emulation case */
+       if (bridge->domain_nr != PCI_DOMAIN_NR_NOT_SET)
+               pci_bus_release_emul_domain_nr(bridge->domain_nr);
+
        kfree(bridge);
 }
 
@@ -1137,7 +1142,8 @@ 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(parent, bus->domain_nr);
+       if (bridge->domain_nr == PCI_DOMAIN_NR_NOT_SET)
+               pci_bus_release_domain_nr(parent, bus->domain_nr);
 #endif
        if (bus_registered)
                put_device(&bus->dev);
index d1fdf81fbe1e427aecbc951fa3fdf65c20450b05..1ef1535802b09c6885cd947229e2c1b58174dc61 100644 (file)
@@ -1956,10 +1956,17 @@ DEFINE_GUARD(pci_dev, struct pci_dev *, pci_dev_lock(_T), pci_dev_unlock(_T))
  */
 #ifdef CONFIG_PCI_DOMAINS
 extern int pci_domains_supported;
+int pci_bus_find_emul_domain_nr(u32 hint, u32 min, u32 max);
+void pci_bus_release_emul_domain_nr(int domain_nr);
 #else
 enum { pci_domains_supported = 0 };
 static inline int pci_domain_nr(struct pci_bus *bus) { return 0; }
 static inline int pci_proc_domain(struct pci_bus *bus) { return 0; }
+static inline int pci_bus_find_emul_domain_nr(u32 hint, u32 min, u32 max)
+{
+       return 0;
+}
+static inline void pci_bus_release_emul_domain_nr(int domain_nr) { }
 #endif /* CONFIG_PCI_DOMAINS */
 
 /*