Hi Bjorn, Arnd and Marc,
This is the v5 for the preparation of virtual PCI support on Hyper-V
ARM64, Previous versions:
v1: https://lore.kernel.org/lkml/20210319161956.2838291-1-boqun.feng@gmail.com/
v2: https://lore.kernel.org/lkml/20210503144635.2297386-1-boqun.feng@gmail.com/
v3: https://lore.kernel.org/lkml/20210609163211.3467449-1-boqun.feng@gmail.com/
v4: https://lore.kernel.org/lkml/20210714102737.198432-1-boqun.feng@gmail.com/
Changes since last version:
* Rebase to 5.14-rc2
* Wording changes (Capitalization and more explanation on why) as
suggested by Bjorn.
* Split the patch #3 in previous into two patches as suggested by
Bjorn.
The basic problem we need to resolve is that ARM64 is an arch with
PCI_DOMAINS_GENERIC=y, so the bus sysdata is pci_config_window. However,
Hyper-V PCI provides a paravirtualized PCI interface, so there is no
actual pci_config_window for a PCI host bridge, so no information can be
retrieve from the pci_config_window of a Hyper-V virtual PCI bus. Also
there is no corresponding ACPI device for the Hyper-V PCI root bridge,
which introduces a special case when trying to find the ACPI device from
the sysdata (see patch #3).
With this patchset, we could enable the virtual PCI on Hyper-V ARM64
guest with other code under development.
Comments and suggestions are welcome.
Regards,
Boqun
Arnd Bergmann (1):
PCI: hv: Generify PCI probing
Boqun Feng (7):
PCI: Introduce domain_nr in pci_host_bridge
PCI: Support populating MSI domains of root buses via bridges
arm64: PCI: Restructure pcibios_root_bridge_prepare()
arm64: PCI: Support root bridge preparation for Hyper-V
PCI: hv: Set ->domain_nr of pci_host_bridge at probing time
PCI: hv: Set up MSI domain at bridge probing time
PCI: hv: Turn on the host bridge probing on ARM64
arch/arm64/kernel/pci.c | 29 +++++++---
drivers/pci/controller/pci-hyperv.c | 86 +++++++++++++++++------------
drivers/pci/probe.c | 12 +++-
include/linux/pci.h | 10 ++++
4 files changed, 92 insertions(+), 45 deletions(-)
--
2.30.2
Since PCI_HYPERV depends on PCI_MSI_IRQ_DOMAIN which selects
GENERIC_MSI_IRQ_DOMAIN, we can use dev_set_msi_domain() to set up the
MSI domain at probing time, and this works for both x86 and ARM64.
Therefore use it as the preparation for ARM64 Hyper-V PCI support.
As a result, no longer need to maintain ->fwnode in x86 specific
pci_sysdata, and make hv_pcibus_device own it instead.
Signed-off-by: Boqun Feng <redacted>
---
drivers/pci/controller/pci-hyperv.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
@@ -450,6 +450,7 @@ enum hv_pcibus_state {structhv_pcibus_device{structpci_sysdatasysdata;structpci_host_bridge*bridge;+structfwnode_handle*fwnode;/* Protocol version negotiated with the host */enumpci_protocol_version_tprotocol_version;enumhv_pcibus_statestate;
@@ -1565,7 +1566,7 @@ static int hv_pcie_init_irq_domain(struct hv_pcibus_device *hbus)hbus->msi_info.handler=handle_edge_irq;hbus->msi_info.handler_name="edge";hbus->msi_info.data=hbus;-hbus->irq_domain=pci_msi_create_irq_domain(hbus->sysdata.fwnode,+hbus->irq_domain=pci_msi_create_irq_domain(hbus->fwnode,&hbus->msi_info,x86_vector_domain);if(!hbus->irq_domain){
@@ -1574,6 +1575,8 @@ static int hv_pcie_init_irq_domain(struct hv_pcibus_device *hbus)return-ENODEV;}+dev_set_msi_domain(&hbus->bridge->dev,hbus->irq_domain);+return0;}
@@ -3118,9 +3121,9 @@ static int hv_pci_probe(struct hv_device *hdev,gotounmap;}-hbus->sysdata.fwnode=irq_domain_alloc_named_fwnode(name);+hbus->fwnode=irq_domain_alloc_named_fwnode(name);kfree(name);-if(!hbus->sysdata.fwnode){+if(!hbus->fwnode){ret=-ENOMEM;gotounmap;}
@@ -3198,7 +3201,7 @@ static int hv_pci_probe(struct hv_device *hdev,free_irq_domain:irq_domain_remove(hbus->irq_domain);free_fwnode:-irq_domain_free_fwnode(hbus->sysdata.fwnode);+irq_domain_free_fwnode(hbus->fwnode);unmap:iounmap(hbus->cfg_addr);free_config:
@@ -3314,7 +3317,7 @@ static int hv_pci_remove(struct hv_device *hdev)hv_free_config_window(hbus);hv_pci_free_bridge_windows(hbus);irq_domain_remove(hbus->irq_domain);-irq_domain_free_fwnode(hbus->sysdata.fwnode);+irq_domain_free_fwnode(hbus->fwnode);hv_put_dom_num(hbus->bridge->domain_nr);
Restructure the pcibios_root_bridge_prepare() as the preparation for
supporting cases when no real ACPI device is related to the PCI host
bridge.
No functional change.
Signed-off-by: Boqun Feng <redacted>
---
arch/arm64/kernel/pci.c | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
Currently we retrieve the PCI domain number of the host bridge from the
bus sysdata (or pci_config_window if PCI_DOMAINS_GENERIC=y). Actually
we have the information at PCI host bridge probing time, and it makes
sense that we store it into pci_host_bridge. One benefit of doing so is
the requirement for supporting PCI on Hyper-V for ARM64, because the
host bridge of Hyper-V doesn't have pci_config_window, whereas ARM64 is
a PCI_DOMAINS_GENERIC=y arch, so we cannot retrieve the PCI domain
number from pci_config_window on ARM64 Hyper-V guest.
As the preparation for ARM64 Hyper-V PCI support, we introduce the
domain_nr in pci_host_bridge and a sentinel value to allow drivers to
set domain numbers properly at probing time. Currently
CONFIG_PCI_DOMAINS_GENERIC=y archs are only users of this
newly-introduced field.
Signed-off-by: Boqun Feng <redacted>
---
drivers/pci/probe.c | 6 +++++-
include/linux/pci.h | 10 ++++++++++
2 files changed, 15 insertions(+), 1 deletion(-)
Currently at root bridge preparation, the corresponding ACPI device will
be set as the companion, however for a Hyper-V virtual PCI root bridge,
there is no corresponding ACPI device, because a Hyper-V virtual PCI
root bridge is discovered via VMBus rather than ACPI table. In order to
support this, we need to make pcibios_root_bridge_prepare() work with
cfg->parent being NULL.
Use a NULL pointer as the ACPI device if there is no corresponding ACPI
device, and this is fine because: 1) ACPI_COMPANION_SET() can work with
the second parameter being NULL, 2) semantically, if a NULL pointer is
set via ACPI_COMPANION_SET(), ACPI_COMPANION() (the read API for this
field) will return NULL, and since ACPI_COMPANION() may return NULL, so
users must have handled the cases where it returns NULL, and 3) since
there is no corresponding ACPI device, it would be wrong to use any
other value here.
Signed-off-by: Boqun Feng <redacted>
---
arch/arm64/kernel/pci.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
Now we have everything we need, just provide a proper sysdata type for
the bus to use on ARM64 and everything else works.
Signed-off-by: Boqun Feng <redacted>
---
drivers/pci/controller/pci-hyperv.c | 7 +++++++
1 file changed, 7 insertions(+)
Currently, at probing time, the MSI domains of root buses are populated
if either the information of MSI domain is available from firmware (DT
or ACPI), or arch-specific sysdata is used to pass the fwnode of the MSI
domain. These two conditions don't cover all, e.g. Hyper-V virtual PCI
on ARM64, which doesn't have the MSI information in the firmware and
couldn't use arch-specific sysdata because running on an architecture
with PCI_DOMAINS_GENERIC=y.
To support populating MSI domains of the root buses at the probing when
neither of the above condition is true, the ->msi_domain of the
corresponding bridge device is used: in pci_host_bridge_msi_domain(),
which should return the MSI domain of the root bus, the ->msi_domain of
the corresponding bridge is fetched first as a potential value of the
MSI domain of the root bus.
In order to use the approach to populate MSI domains, the driver needs
to dev_set_msi_domain() on the bridge before calling
pci_register_host_bridge(), and makes sure GENERIC_MSI_IRQ_DOMAIN=y.
Another advantage of this new approach is providing an arch-independent
way to populate MSI domains, which allows sharing the driver code as
much as possible between architectures.
Originally-by: Arnd Bergmann [off-list ref]
Signed-off-by: Boqun Feng <redacted>
---
drivers/pci/probe.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
@@ -829,11 +829,15 @@ static struct irq_domain *pci_host_bridge_msi_domain(struct pci_bus *bus){structirq_domain*d;+/* If the host bridge driver sets a MSI domain of the bridge, use it */+d=dev_get_msi_domain(bus->bridge);+/**Anyfirmwareinterfacethatcanresolvethemsi_domain*shouldbecalledfromhere.*/-d=pci_host_bridge_of_msi_domain(bus);+if(!d)+d=pci_host_bridge_of_msi_domain(bus);if(!d)d=pci_host_bridge_acpi_msi_domain(bus);
From: Arnd Bergmann <arnd@arndb.de>
In order to support ARM64 Hyper-V PCI, we need to set up the bridge at
probing time because ARM64 is a PCI_DOMAIN_GENERIC=y arch and we don't
have pci_config_window (ARM64 sysdata) for a PCI root bus on Hyper-V, so
it's impossible to retrieve the information (e.g. PCI domains, MSI
domains) from bus sysdata on ARM64 after creation.
Originally in create_root_hv_pci_bus(), pci_create_root_bus() is used to
create the root bus and the corresponding bridge based on x86 sysdata.
Now we create a bridge first and then call pci_scan_root_bus_bridge(),
which allows us to do the necessary set-ups for the bridge.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Boqun Feng <redacted>
---
drivers/pci/controller/pci-hyperv.c | 57 +++++++++++++++--------------
1 file changed, 30 insertions(+), 27 deletions(-)
@@ -449,6 +449,7 @@ enum hv_pcibus_state {structhv_pcibus_device{structpci_sysdatasysdata;+structpci_host_bridge*bridge;/* Protocol version negotiated with the host */enumpci_protocol_version_tprotocol_version;enumhv_pcibus_statestate;
@@ -1850,21 +1849,22 @@ static void hv_pci_assign_numa_node(struct hv_pcibus_device *hbus)*/staticintcreate_root_hv_pci_bus(structhv_pcibus_device*hbus){-/* Register the device */-hbus->pci_bus=pci_create_root_bus(&hbus->hdev->device,-0,/* bus number is always zero */-&hv_pcifront_ops,-&hbus->sysdata,-&hbus->resources_for_children);-if(!hbus->pci_bus)-return-ENODEV;+interror;+structpci_host_bridge*bridge=hbus->bridge;++bridge->dev.parent=&hbus->hdev->device;+bridge->sysdata=&hbus->sysdata;+bridge->ops=&hv_pcifront_ops;++error=pci_scan_root_bus_bridge(bridge);+if(error)+returnerror;pci_lock_rescan_remove();-pci_scan_child_bus(hbus->pci_bus);hv_pci_assign_numa_node(hbus);-pci_bus_assign_resources(hbus->pci_bus);+pci_bus_assign_resources(bridge->bus);hv_pci_assign_slots(hbus);-pci_bus_add_devices(hbus->pci_bus);+pci_bus_add_devices(bridge->bus);pci_unlock_rescan_remove();hbus->state=hv_pcibus_installed;return0;
@@ -2662,8 +2662,7 @@ static int hv_pci_allocate_bridge_windows(struct hv_pcibus_device *hbus)/* Modify this resource to become a bridge window. */hbus->low_mmio_res->flags|=IORESOURCE_WINDOW;hbus->low_mmio_res->flags&=~IORESOURCE_BUSY;-pci_add_resource(&hbus->resources_for_children,-hbus->low_mmio_res);+pci_add_resource(&hbus->bridge->windows,hbus->low_mmio_res);}if(hbus->high_mmio_space){
@@ -2682,8 +2681,7 @@ static int hv_pci_allocate_bridge_windows(struct hv_pcibus_device *hbus)/* Modify this resource to become a bridge window. */hbus->high_mmio_res->flags|=IORESOURCE_WINDOW;hbus->high_mmio_res->flags&=~IORESOURCE_BUSY;-pci_add_resource(&hbus->resources_for_children,-hbus->high_mmio_res);+pci_add_resource(&hbus->bridge->windows,hbus->high_mmio_res);}return0;
@@ -3014,6 +3013,10 @@ static int hv_pci_probe(struct hv_device *hdev,*/BUILD_BUG_ON(sizeof(*hbus)>HV_HYP_PAGE_SIZE);+bridge=devm_pci_alloc_host_bridge(&hdev->device,0);+if(!bridge)+return-ENOMEM;+/**Withtherecent59bb47985c1d("mm, sl[aou]b: guarantee natural*alignmentforkmalloc(power-of-two)"), kzalloc() is able to allocate
@@ -3035,6 +3038,8 @@ static int hv_pci_probe(struct hv_device *hdev,hbus=kzalloc(HV_HYP_PAGE_SIZE,GFP_KERNEL);if(!hbus)return-ENOMEM;++hbus->bridge=bridge;hbus->state=hv_pcibus_init;hbus->wslot_res_allocated=-1;
@@ -3071,7 +3076,6 @@ static int hv_pci_probe(struct hv_device *hdev,hbus->hdev=hdev;INIT_LIST_HEAD(&hbus->children);INIT_LIST_HEAD(&hbus->dr_list);-INIT_LIST_HEAD(&hbus->resources_for_children);spin_lock_init(&hbus->config_lock);spin_lock_init(&hbus->device_list_lock);spin_lock_init(&hbus->retarget_msi_interrupt_lock);
@@ -3295,9 +3299,9 @@ static int hv_pci_remove(struct hv_device *hdev)/* Remove the bus from PCI's point of view. */pci_lock_rescan_remove();-pci_stop_root_bus(hbus->pci_bus);+pci_stop_root_bus(hbus->bridge->bus);hv_pci_remove_slots(hbus);-pci_remove_root_bus(hbus->pci_bus);+pci_remove_root_bus(hbus->bridge->bus);pci_unlock_rescan_remove();}
@@ -3307,7 +3311,6 @@ static int hv_pci_remove(struct hv_device *hdev)iounmap(hbus->cfg_addr);hv_free_config_window(hbus);-pci_free_resource_list(&hbus->resources_for_children);hv_pci_free_bridge_windows(hbus);irq_domain_remove(hbus->irq_domain);irq_domain_free_fwnode(hbus->sysdata.fwnode);
No functional change, just store and maintain the PCI domain number in
the ->domain_nr of pci_host_bridge. Note that we still need to keep
the copy of domain number in x86-specific pci_sysdata, because x86 is
not a PCI_DOMAINS_GENERIC=y architecture, so the ->domain_nr of
pci_host_bridge doesn't work for it yet.
Signed-off-by: Boqun Feng <redacted>
---
drivers/pci/controller/pci-hyperv.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
@@ -3071,6 +3071,7 @@ static int hv_pci_probe(struct hv_device *hdev,"PCI dom# 0x%hx has collision, using 0x%hx",dom_req,dom);+hbus->bridge->domain_nr=dom;hbus->sysdata.domain=dom;hbus->hdev=hdev;
@@ -3080,7 +3081,7 @@ static int hv_pci_probe(struct hv_device *hdev,spin_lock_init(&hbus->device_list_lock);spin_lock_init(&hbus->retarget_msi_interrupt_lock);hbus->wq=alloc_ordered_workqueue("hv_pci_%x",0,-hbus->sysdata.domain);+hbus->bridge->domain_nr);if(!hbus->wq){ret=-ENOMEM;gotofree_dom;
@@ -3207,7 +3208,7 @@ static int hv_pci_probe(struct hv_device *hdev,destroy_wq:destroy_workqueue(hbus->wq);free_dom:-hv_put_dom_num(hbus->sysdata.domain);+hv_put_dom_num(hbus->bridge->domain_nr);free_bus:kfree(hbus);returnret;
@@ -3315,7 +3316,7 @@ static int hv_pci_remove(struct hv_device *hdev)irq_domain_remove(hbus->irq_domain);irq_domain_free_fwnode(hbus->sysdata.fwnode);-hv_put_dom_num(hbus->sysdata.domain);+hv_put_dom_num(hbus->bridge->domain_nr);kfree(hbus);returnret;
Now we have everything we need, just provide a proper sysdata type for
the bus to use on ARM64 and everything else works.
Signed-off-by: Boqun Feng <redacted>
---
drivers/pci/controller/pci-hyperv.c | 7 +++++++
1 file changed, 7 insertions(+)
Am I the only one who find this rather odd? Nothing ever populates
this data structure on arm64, and its only purpose seems to serve as
an anchor to retrieve the hbus via container_of().
If that's indeed the case, I'd rather see an arch-specific to_hbus()
helper that uses another (preexisting) field as the anchor for arm64.
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
Now we have everything we need, just provide a proper sysdata type for
the bus to use on ARM64 and everything else works.
Signed-off-by: Boqun Feng <redacted>
---
drivers/pci/controller/pci-hyperv.c | 7 +++++++
1 file changed, 7 insertions(+)
Am I the only one who find this rather odd? Nothing ever populates
this data structure on arm64, and its only purpose seems to serve as
an anchor to retrieve the hbus via container_of().
This field will also be used as the ->sysdata of pci_bus and
pci_host_bridge, and some of the PCI core code touches. Although I made
this field as all zeroed and make sure PCI core can handle (patch #4).
If that's indeed the case, I'd rather see an arch-specific to_hbus()
helper that uses another (preexisting) field as the anchor for arm64.
I did a quick look, but I didn't find another field works: the field
needs to be placed inside hv_pcibus_device and the address can be
retrieved via pci_bus. I'm open to any suggestion in case that I missed
something.
Regards,
Boqun
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
Now we have everything we need, just provide a proper sysdata type for
the bus to use on ARM64 and everything else works.
Signed-off-by: Boqun Feng <redacted>
---
drivers/pci/controller/pci-hyperv.c | 7 +++++++
1 file changed, 7 insertions(+)
Am I the only one who find this rather odd? Nothing ever populates
this data structure on arm64, and its only purpose seems to serve as
an anchor to retrieve the hbus via container_of().
This field will also be used as the ->sysdata of pci_bus and
pci_host_bridge, and some of the PCI core code touches. Although I made
this field as all zeroed and make sure PCI core can handle (patch #4).
Huh, I see. I missed this particular nugget. This is so convoluted...
quoted
If that's indeed the case, I'd rather see an arch-specific to_hbus()
helper that uses another (preexisting) field as the anchor for arm64.
I did a quick look, but I didn't find another field works: the field
needs to be placed inside hv_pcibus_device and the address can be
retrieved via pci_bus. I'm open to any suggestion in case that I missed
something.
No, the above pretty much kills my suggestion.
Thanks for the explanation,
M.
--
Without deviation from the norm, progress is not possible.
On Tue, Jul 20, 2021 at 09:44:22PM +0800, Boqun Feng wrote:
Currently we retrieve the PCI domain number of the host bridge from the
bus sysdata (or pci_config_window if PCI_DOMAINS_GENERIC=y). Actually
we have the information at PCI host bridge probing time, and it makes
sense that we store it into pci_host_bridge. One benefit of doing so is
the requirement for supporting PCI on Hyper-V for ARM64, because the
host bridge of Hyper-V doesn't have pci_config_window, whereas ARM64 is
a PCI_DOMAINS_GENERIC=y arch, so we cannot retrieve the PCI domain
number from pci_config_window on ARM64 Hyper-V guest.
As the preparation for ARM64 Hyper-V PCI support, we introduce the
domain_nr in pci_host_bridge and a sentinel value to allow drivers to
set domain numbers properly at probing time. Currently
CONFIG_PCI_DOMAINS_GENERIC=y archs are only users of this
newly-introduced field.
Signed-off-by: Boqun Feng <redacted>
Once all the issues are ironed out, Lorenzo should probably merge this
since it's primarily Hyper-V stuff, but I'm OK with this part:
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
But fix the comment issue below.
On Tue, Jul 20, 2021 at 05:49:25PM -0500, Bjorn Helgaas wrote:
On Tue, Jul 20, 2021 at 09:44:22PM +0800, Boqun Feng wrote:
quoted
Currently we retrieve the PCI domain number of the host bridge from the
bus sysdata (or pci_config_window if PCI_DOMAINS_GENERIC=y). Actually
we have the information at PCI host bridge probing time, and it makes
sense that we store it into pci_host_bridge. One benefit of doing so is
the requirement for supporting PCI on Hyper-V for ARM64, because the
host bridge of Hyper-V doesn't have pci_config_window, whereas ARM64 is
a PCI_DOMAINS_GENERIC=y arch, so we cannot retrieve the PCI domain
number from pci_config_window on ARM64 Hyper-V guest.
As the preparation for ARM64 Hyper-V PCI support, we introduce the
domain_nr in pci_host_bridge and a sentinel value to allow drivers to
set domain numbers properly at probing time. Currently
CONFIG_PCI_DOMAINS_GENERIC=y archs are only users of this
newly-introduced field.
Signed-off-by: Boqun Feng <redacted>
Once all the issues are ironed out, Lorenzo should probably merge this
since it's primarily Hyper-V stuff, but I'm OK with this part:
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Thanks!
But fix the comment issue below.
Just send a v5.1 for this patch with the comment fixed and your
Acked-by.
Regards,
Boqun