From: Yu Zhang <[email protected]> Sent: Wednesday, July 8, 2026 6:09 AM > > On Wed, Jul 08, 2026 at 02:52:43AM +0000, Michael Kelley wrote: > > From: Yu Zhang <[email protected]> Sent: Thursday, July 2, 2026 > > 9:05 AM > > > > > > From: Easwar Hariharan <[email protected]> > > > > > > Hyper-V identifies each PCI pass-thru device by a logical device ID in > > > its hypercall interface. This ID consists of a per-bus prefix, derived > > > from the VMBus device instance GUID, combined with the PCI function > > > number of the endpoint device. > > > > > > Add a small registry in hv_common.c that maps a PCI domain number to its > > > logical device ID prefix. The vPCI bus driver (pci-hyperv) registers the > > > prefix when a bus is probed and unregisters it when the bus is removed. > > > Consumers such as the para-virtualized IOMMU driver look up the prefix > > > by PCI domain number and combine it with the function number to form the > > > complete logical device ID for hypercalls. > > > > > > The prefix construction is shared via hv_build_logical_dev_id_prefix() so > > > that pci-hyperv's interrupt retargeting path and the registry use exactly > > > the same byte layout. It is derived on demand from the constant hv_device > > > instance GUID rather than cached in struct hv_pcibus_device, which is > > > private to the pci-hyperv module; this keeps the interface narrow and > > > avoids depending on pci-hyperv internals. > > > > > > Co-developed-by: Yu Zhang <[email protected]> > > > Signed-off-by: Yu Zhang <[email protected]> > > > Signed-off-by: Easwar Hariharan <[email protected]> > > > --- > > > drivers/hv/hv_common.c | 95 +++++++++++++++++++++++++++++ > > > drivers/pci/controller/pci-hyperv.c | 21 +++++-- > > > include/asm-generic/mshyperv.h | 13 ++++ > > > include/linux/hyperv.h | 8 +++ > > > 4 files changed, 132 insertions(+), 5 deletions(-) > > > > > > diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c > > > index 6b67ac616789..53493f8d14dc 100644 > > > --- a/drivers/hv/hv_common.c > > > +++ b/drivers/hv/hv_common.c > > > @@ -26,6 +26,8 @@ > > > #include <linux/kmsg_dump.h> > > > #include <linux/sizes.h> > > > #include <linux/slab.h> > > > +#include <linux/list.h> > > > +#include <linux/spinlock.h> > > > #include <linux/dma-map-ops.h> > > > #include <linux/set_memory.h> > > > #include <hyperv/hvhdk.h> > > > @@ -863,3 +865,96 @@ const char *hv_result_to_string(u64 status) > > > return "Unknown"; > > > } > > > EXPORT_SYMBOL_GPL(hv_result_to_string); > > > + > > > +#ifdef CONFIG_HYPERV_PVIOMMU > > > +/* > > > + * Logical device ID registry shared between the vPCI bus driver > > > + * (pci-hyperv) and the para-virtualized IOMMU driver. The vPCI driver > > > + * registers the per-bus logical device ID prefix at bus probe time, and > > > + * the pvIOMMU driver looks it up to build the full logical device ID > > > used > > > + * in IOMMU hypercalls. > > > + */ > > > +struct hv_pci_busdata { > > > + int pci_domain_nr; > > > + u32 logical_dev_id_prefix; > > > + struct list_head list; > > > +}; > > > + > > > +static LIST_HEAD(hv_pci_bus_list); > > > +static DEFINE_SPINLOCK(hv_pci_bus_lock); > > > + > > > +int hv_iommu_register_pci_bus(int pci_domain_nr, u32 > > > logical_dev_id_prefix) > > > +{ > > > + struct hv_pci_busdata *bus, *new; > > > + int ret = 0; > > > + > > > + new = kzalloc_obj(*new, GFP_KERNEL); > > > + if (!new) > > > + return -ENOMEM; > > > + > > > + spin_lock(&hv_pci_bus_lock); > > > + list_for_each_entry(bus, &hv_pci_bus_list, list) { > > > + if (bus->pci_domain_nr != pci_domain_nr) > > > + continue; > > > + > > > + if (bus->logical_dev_id_prefix != logical_dev_id_prefix) { > > > + pr_err("stale registration for PCI domain %d (old > > > prefix 0x%08x, new 0x%08x)\n", > > > + pci_domain_nr, bus->logical_dev_id_prefix, > > > + logical_dev_id_prefix); > > > + ret = -EEXIST; > > > + } > > > + > > > + goto out_free; > > > + } > > > + > > > + new->pci_domain_nr = pci_domain_nr; > > > + new->logical_dev_id_prefix = logical_dev_id_prefix; > > > + list_add(&new->list, &hv_pci_bus_list); > > > + spin_unlock(&hv_pci_bus_lock); > > > + return 0; > > > + > > > +out_free: > > > + spin_unlock(&hv_pci_bus_lock); > > > + kfree(new); > > > + return ret; > > > +} > > > +EXPORT_SYMBOL_FOR_MODULES(hv_iommu_register_pci_bus, "pci-hyperv"); > > > + > > > +void hv_iommu_unregister_pci_bus(int pci_domain_nr) > > > +{ > > > + struct hv_pci_busdata *bus, *tmp; > > > + > > > + spin_lock(&hv_pci_bus_lock); > > > + list_for_each_entry_safe(bus, tmp, &hv_pci_bus_list, list) { > > > + if (bus->pci_domain_nr == pci_domain_nr) { > > > + list_del(&bus->list); > > > + kfree(bus); > > > + break; > > > + } > > > + } > > > + spin_unlock(&hv_pci_bus_lock); > > > +} > > > +EXPORT_SYMBOL_FOR_MODULES(hv_iommu_unregister_pci_bus, "pci-hyperv"); > > > + > > > +/* > > > + * Look up the logical device ID prefix registered for @pci_domain_nr. > > > + * Returns 0 on success with *prefix filled in; -ENODEV if no entry is > > > + * registered for that PCI domain. > > > + */ > > > +int hv_iommu_lookup_logical_dev_id(int pci_domain_nr, u32 *prefix) > > > +{ > > > + struct hv_pci_busdata *bus; > > > + int ret = -ENODEV; > > > + > > > + spin_lock(&hv_pci_bus_lock); > > > + list_for_each_entry(bus, &hv_pci_bus_list, list) { > > > + if (bus->pci_domain_nr == pci_domain_nr) { > > > + *prefix = bus->logical_dev_id_prefix; > > > + ret = 0; > > > + break; > > > + } > > > + } > > > + spin_unlock(&hv_pci_bus_lock); > > > + return ret; > > > +} > > > > I started thinking about the mechanism here because it's somewhat > > annoying that it takes 77 lines of code (sans comments) to manage > > this simple little mapping. I also started thinking about how many entries > > are likely to be in the mapping. A guest VM probably has fewer than 10 > > entries unless it has multiple NICs and maybe some GPUs. But this code > > is also intended to be used by the Linux-as-root code, and I'm thinking > > that the number of PCI devices managed by the root could easily be a > > hundred or more if the root is managing a couple dozen VMs on a large > > physical server. Searching a linked list with 100 or more entries could be > > a bit slow. > > > > If only the guest scenario were needed, you could declare a static > > array with 64 entries (64 is an arbitrary upper bound), and just search > > through the array instead of having to allocate memory, deal with > > allocation failures, and deal with linked lists. But a fixed size array > > would need to be much bigger for the root scenario, and you would > > still be doing a linear search. > > > > A better alternative to consider is rhashtable, which is an existing > > Linux kernel facility. Based on what an AI bot generated for me, the > > code for setting up and using rhashtable is straightforward, and > > would probably result in far fewer than 77 lines of code. Lookups > > would also be faster than a linear search, at least for the root case > > with more than just a few entries. I'd suggest looking at rhashtable > > to see whether you like how the resulting code comes out for this > > use case, and whether it really is simpler than a roll-your-own linked > > list. > > > > Thank you so much for thinking this through, Michael! That is really > a valid concern. > > How about using XArray? It might be more lightweight compared with > rhashtable. Using pci_domain_nr as the key and prefix as the value. > Maybe sth. like: > > ┃ > static DEFINE_XARRAY(hv_pci_bus_xa); > > int hv_iommu_register_pci_bus(int domain_nr, u32 prefix) > { > return xa_insert(&hv_pci_bus_xa, domain_nr, > xa_mk_value(prefix), GFP_KERNEL); > } > ┃ > > void hv_iommu_unregister_pci_bus(int domain_nr) > { > xa_erase(&hv_pci_bus_xa, domain_nr); > } > > int hv_iommu_lookup_logical_dev_id(int domain_nr, u32 *prefix) > { > void *entry = xa_load(&hv_pci_bus_xa, domain_nr); > if (!entry) > ┃ > return -ENODEV; > *prefix = xa_to_value(entry); > return 0; > ┃ > } >
xarray is best where the keys are dense or mostly dense integers. In this use case, the pci_domain_nr keys are essentially random 16-bit values, which doesn't fit xarray as well. It would work, but wouldn't be very efficient. See the 2nd paragraph of the documentation here: https://www.kernel.org/doc/html/latest/core-api/xarray.html#xarray Michael

