mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 18:13:41 +02:00
PCI: Enable host bridge emulation for PCI_DOMAINS_GENERIC platforms
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 [1] 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
This commit is contained in:
parent
3a86608788
commit
bcce8c74f1
|
|
@ -3696,48 +3696,6 @@ static int hv_send_resources_released(struct hv_device *hdev)
|
||||||
return 0;
|
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
|
* hv_pci_probe() - New VMBus channel probe, for a root PCI bus
|
||||||
* @hdev: VMBus's tracking struct for this 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 pci_host_bridge *bridge;
|
||||||
struct hv_pcibus_device *hbus;
|
struct hv_pcibus_device *hbus;
|
||||||
u16 dom_req, dom;
|
int ret, dom;
|
||||||
|
u16 dom_req;
|
||||||
char *name;
|
char *name;
|
||||||
int ret;
|
|
||||||
|
|
||||||
bridge = devm_pci_alloc_host_bridge(&hdev->device, 0);
|
bridge = devm_pci_alloc_host_bridge(&hdev->device, 0);
|
||||||
if (!bridge)
|
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.
|
* PCI bus (which is actually emulated by the hypervisor) is domain 0.
|
||||||
* (2) There will be no overlap between domains (after fixing possible
|
* (2) There will be no overlap between domains (after fixing possible
|
||||||
* collisions) in the same VM.
|
* 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_req = hdev->dev_instance.b[5] << 8 | hdev->dev_instance.b[4];
|
||||||
dom = hv_get_dom_num(dom_req);
|
dom = pci_bus_find_emul_domain_nr(dom_req, 1, U16_MAX);
|
||||||
|
if (dom < 0) {
|
||||||
if (dom == HVPCI_DOM_INVALID) {
|
|
||||||
dev_err(&hdev->device,
|
dev_err(&hdev->device,
|
||||||
"Unable to use dom# 0x%x or other numbers", dom_req);
|
"Unable to use dom# 0x%x or other numbers", dom_req);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
|
@ -3917,7 +3878,7 @@ static int hv_pci_probe(struct hv_device *hdev,
|
||||||
destroy_wq:
|
destroy_wq:
|
||||||
destroy_workqueue(hbus->wq);
|
destroy_workqueue(hbus->wq);
|
||||||
free_dom:
|
free_dom:
|
||||||
hv_put_dom_num(hbus->bridge->domain_nr);
|
pci_bus_release_emul_domain_nr(hbus->bridge->domain_nr);
|
||||||
free_bus:
|
free_bus:
|
||||||
kfree(hbus);
|
kfree(hbus);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -4042,8 +4003,6 @@ static void hv_pci_remove(struct hv_device *hdev)
|
||||||
irq_domain_remove(hbus->irq_domain);
|
irq_domain_remove(hbus->irq_domain);
|
||||||
irq_domain_free_fwnode(hbus->fwnode);
|
irq_domain_free_fwnode(hbus->fwnode);
|
||||||
|
|
||||||
hv_put_dom_num(hbus->bridge->domain_nr);
|
|
||||||
|
|
||||||
kfree(hbus);
|
kfree(hbus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4217,9 +4176,6 @@ static int __init init_hv_pci_drv(void)
|
||||||
if (ret)
|
if (ret)
|
||||||
return 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 */
|
/* Initialize PCI block r/w interface */
|
||||||
hvpci_block_ops.read_block = hv_read_config_block;
|
hvpci_block_ops.read_block = hv_read_config_block;
|
||||||
hvpci_block_ops.write_block = hv_write_config_block;
|
hvpci_block_ops.write_block = hv_write_config_block;
|
||||||
|
|
|
||||||
|
|
@ -6656,9 +6656,31 @@ static void pci_no_domains(void)
|
||||||
#endif
|
#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
|
#ifdef CONFIG_PCI_DOMAINS_GENERIC
|
||||||
static DEFINE_IDA(pci_domain_nr_static_ida);
|
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)
|
static void of_pci_reserve_static_domain_nr(void)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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->windows);
|
||||||
pci_free_resource_list(&bridge->dma_ranges);
|
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);
|
kfree(bridge);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1137,6 +1142,7 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
|
||||||
device_del(&bridge->dev);
|
device_del(&bridge->dev);
|
||||||
free:
|
free:
|
||||||
#ifdef CONFIG_PCI_DOMAINS_GENERIC
|
#ifdef CONFIG_PCI_DOMAINS_GENERIC
|
||||||
|
if (bridge->domain_nr == PCI_DOMAIN_NR_NOT_SET)
|
||||||
pci_bus_release_domain_nr(parent, bus->domain_nr);
|
pci_bus_release_domain_nr(parent, bus->domain_nr);
|
||||||
#endif
|
#endif
|
||||||
if (bus_registered)
|
if (bus_registered)
|
||||||
|
|
|
||||||
|
|
@ -1956,10 +1956,17 @@ DEFINE_GUARD(pci_dev, struct pci_dev *, pci_dev_lock(_T), pci_dev_unlock(_T))
|
||||||
*/
|
*/
|
||||||
#ifdef CONFIG_PCI_DOMAINS
|
#ifdef CONFIG_PCI_DOMAINS
|
||||||
extern int pci_domains_supported;
|
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
|
#else
|
||||||
enum { pci_domains_supported = 0 };
|
enum { pci_domains_supported = 0 };
|
||||||
static inline int pci_domain_nr(struct pci_bus *bus) { return 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_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 */
|
#endif /* CONFIG_PCI_DOMAINS */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user