From: Dimitri Daskalakis <[email protected]> Modify pci_init_capabilities() to discover the SIOV extended capability (cap ID 0x38). When present, allocate struct pci_siov that records the capability position, total SDI count, routing ID offset and stride, and the maximum bus range the SDIs can span.
The init path mirrors sriov_init(): read the capability registers, compute the worst-case bus consumption from total_SDIs, and stash the result in the PF's pci_dev. Release frees the structure on teardown. If is_physfn was already set (by sriov_init), it will not be cleared if siov_init() fails. This prevents clobbering the flag for devices that enable both virtualization types. The SR-IOV code does not unset the is_physfn bit of a pci device when disabled, and the SIOV code follows that pattern. Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Dimitri Daskalakis <[email protected]> --- drivers/pci/Makefile | 1 + drivers/pci/pci.h | 16 ++++++ drivers/pci/probe.c | 2 + drivers/pci/siov.c | 113 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+) create mode 100644 drivers/pci/siov.c diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 41ebc3b9a518..a584cd1bf08a 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_PCI_QUIRKS) += quirks.o obj-$(CONFIG_HOTPLUG_PCI) += hotplug/ obj-$(CONFIG_PCI_ATS) += ats.o obj-$(CONFIG_PCI_IOV) += iov.o +obj-$(CONFIG_PCI_SIOV) += siov.o obj-$(CONFIG_PCI_BRIDGE_EMUL) += pci-bridge-emul.o obj-$(CONFIG_PCI_LABEL) += pci-label.o obj-$(CONFIG_X86_INTEL_MID) += pci-mid.o diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index fd7c04e26c16..a516db996aab 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -1042,6 +1042,22 @@ static inline u16 pci_virtfn_routing_id(struct pci_dev *pf, u16 offset, } #endif +#ifdef CONFIG_PCI_SIOV +int pci_siov_init(struct pci_dev *dev); +void pci_siov_release(struct pci_dev *dev); +int pci_siov_bus_range(struct pci_bus *bus); +#else +static inline int pci_siov_init(struct pci_dev *dev) +{ + return -ENODEV; +} +static inline void pci_siov_release(struct pci_dev *dev) { } +static inline int pci_siov_bus_range(struct pci_bus *bus) +{ + return 0; +} +#endif + #ifdef CONFIG_PCIE_TPH void pci_restore_tph_state(struct pci_dev *dev); void pci_save_tph_state(struct pci_dev *dev); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b63cd0c310bc..bebc32c8d374 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2473,6 +2473,7 @@ static void pci_release_capabilities(struct pci_dev *dev) pci_aer_exit(dev); pci_rcec_exit(dev); pci_iov_release(dev); + pci_siov_release(dev); pci_free_cap_save_buffers(dev); } @@ -2666,6 +2667,7 @@ static void pci_init_capabilities(struct pci_dev *dev) pci_vpd_init(dev); /* Vital Product Data */ pci_configure_ari(dev); /* Alternative Routing-ID Forwarding */ pci_iov_init(dev); /* Single Root I/O Virtualization */ + pci_siov_init(dev); /* Scalable I/O Virtualization */ pci_ats_init(dev); /* Address Translation Services */ pci_pri_init(dev); /* Page Request Interface */ pci_pasid_init(dev); /* Process Address Space ID */ diff --git a/drivers/pci/siov.c b/drivers/pci/siov.c new file mode 100644 index 000000000000..7372ce95714b --- /dev/null +++ b/drivers/pci/siov.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCI Express Scalable I/O Virtualization (SIOV) support + */ + +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/export.h> +#include "pci.h" + +static int pci_siov_sdi_bus(struct pci_dev *dev, int sdi_id) +{ + if (!dev->siov) + return -EINVAL; + return pci_virtfn_routing_id(dev, dev->siov->offset, + dev->siov->stride, sdi_id) >> 8; +} + +static int compute_max_sdi_buses(struct pci_dev *dev) +{ + struct pci_siov *siov = dev->siov; + + if (!siov->offset || (siov->total_SDIs > 1 && !siov->stride)) + return -EIO; + + siov->max_SDI_buses = pci_siov_sdi_bus(dev, siov->total_SDIs - 1); + return 0; +} + +static int siov_init(struct pci_dev *dev, int pos) +{ + struct pci_siov *siov; + bool was_physfn; + u16 total; + u8 status; + int rc; + + pci_read_config_byte(dev, pos + PCI_SIOV_STATUS, &status); + if (status & PCI_SIOV_STATUS_ENABLED) + pci_warn(dev, "SIOV: SDIs active at init, FLR may be required\n"); + + pci_read_config_word(dev, pos + PCI_SIOV_TOTAL_SDI, &total); + if (!total) + return 0; + + siov = kzalloc_obj(*siov); + if (!siov) + return -ENOMEM; + + siov->pos = pos; + siov->total_SDIs = total; + siov->driver_max_SDIs = total; + siov->self = dev; + pci_read_config_dword(dev, pos + PCI_SIOV_CAP, &siov->cap); + pci_read_config_word(dev, pos + PCI_SIOV_SDI_OFFSET, &siov->offset); + pci_read_config_word(dev, pos + PCI_SIOV_SDI_STRIDE, &siov->stride); + + was_physfn = dev->is_physfn; + + dev->siov = siov; + dev->is_physfn = 1; + dev->is_siov = 1; + rc = compute_max_sdi_buses(dev); + if (rc) { + dev->siov = NULL; + dev->is_siov = 0; + if (!was_physfn) + dev->is_physfn = 0; + kfree(siov); + return rc; + } + + return 0; +} + +static void siov_release(struct pci_dev *dev) +{ + WARN_ON_ONCE(dev->siov->num_SDIs); + + kfree(dev->siov); + dev->siov = NULL; + dev->is_siov = 0; +} + +/** + * pci_siov_init - initialize the Scalable IOV capability + * @dev: the PCI device + * + * Returns 0 on success, or negative on failure. + */ +int pci_siov_init(struct pci_dev *dev) +{ + int pos; + + if (!pci_is_pcie(dev)) + return -ENODEV; + + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SIOV); + if (pos) + return siov_init(dev, pos); + + return -ENODEV; +} + +/** + * pci_siov_release - release resources used by the SIOV capability + * @dev: the PCI device + */ +void pci_siov_release(struct pci_dev *dev) +{ + if (dev->siov) + siov_release(dev); +} -- 2.52.0
