Merge branch 'pci/enumeration'

- Enable host bridge emulation for PCI_DOMAINS_GENERIC platforms (Dan
  Williams)

- Switch vmd from custom domain number allocator to the common allocator
  (Dan Williams)

* pci/enumeration:
  PCI: vmd: Switch to pci_bus_find_emul_domain_nr()
  PCI: Enable host bridge emulation for PCI_DOMAINS_GENERIC platforms
This commit is contained in:
Bjorn Helgaas 2025-12-03 14:18:30 -06:00
commit e0910b31eb
5 changed files with 63 additions and 78 deletions

View 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;

View File

@ -565,22 +565,6 @@ static void vmd_detach_resources(struct vmd_dev *vmd)
vmd->dev->resource[VMD_MEMBAR2].child = NULL;
}
/*
* VMD domains start at 0x10000 to not clash with ACPI _SEG domains.
* Per ACPI r6.0, sec 6.5.6, _SEG returns an integer, of which the lower
* 16 bits are the PCI Segment Group (domain) number. Other bits are
* currently reserved.
*/
static int vmd_find_free_domain(void)
{
int domain = 0xffff;
struct pci_bus *bus = NULL;
while ((bus = pci_find_next_bus(bus)) != NULL)
domain = max_t(int, domain, pci_domain_nr(bus));
return domain + 1;
}
static int vmd_get_phys_offsets(struct vmd_dev *vmd, bool native_hint,
resource_size_t *offset1,
resource_size_t *offset2)
@ -865,13 +849,6 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
.parent = res,
};
sd->vmd_dev = vmd->dev;
sd->domain = vmd_find_free_domain();
if (sd->domain < 0)
return sd->domain;
sd->node = pcibus_to_node(vmd->dev->bus);
/*
* Currently MSI remapping must be enabled in guest passthrough mode
* due to some missing interrupt remapping plumbing. This is probably
@ -897,9 +874,24 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
pci_add_resource_offset(&resources, &vmd->resources[1], offset[0]);
pci_add_resource_offset(&resources, &vmd->resources[2], offset[1]);
sd->vmd_dev = vmd->dev;
/*
* Emulated domains start at 0x10000 to not clash with ACPI _SEG
* domains. Per ACPI r6.0, sec 6.5.6, _SEG returns an integer, of
* which the lower 16 bits are the PCI Segment Group (domain) number.
* Other bits are currently reserved.
*/
sd->domain = pci_bus_find_emul_domain_nr(0, 0x10000, INT_MAX);
if (sd->domain < 0)
return sd->domain;
sd->node = pcibus_to_node(vmd->dev->bus);
vmd->bus = pci_create_root_bus(&vmd->dev->dev, vmd->busn_start,
&vmd_ops, sd, &resources);
if (!vmd->bus) {
pci_bus_release_emul_domain_nr(sd->domain);
pci_free_resource_list(&resources);
vmd_remove_irq_domain(vmd);
return -ENODEV;
@ -992,6 +984,7 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
return -ENOMEM;
vmd->dev = dev;
vmd->sysdata.domain = PCI_DOMAIN_NR_NOT_SET;
vmd->instance = ida_alloc(&vmd_instance_ida, GFP_KERNEL);
if (vmd->instance < 0)
return vmd->instance;
@ -1057,6 +1050,7 @@ static void vmd_remove(struct pci_dev *dev)
vmd_detach_resources(vmd);
vmd_remove_irq_domain(vmd);
ida_free(&vmd_instance_ida, vmd->instance);
pci_bus_release_emul_domain_nr(vmd->sysdata.domain);
}
static void vmd_shutdown(struct pci_dev *dev)

View 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)
{

View 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);

View 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 */
/*